pax_global_header00006660000000000000000000000064140026352530014512gustar00rootroot0000000000000052 comment=3caacd051c28aabb74c50e6ff9eb578bf8f5fa9b libinstpatch-1.1.6/000077500000000000000000000000001400263525300142035ustar00rootroot00000000000000libinstpatch-1.1.6/ABOUT-NLS000066400000000000000000001302351400263525300154360ustar00rootroot00000000000000Notes on the Free Translation Project ************************************* Free software is going international! The Free Translation Project is a way to get maintainers of free software, translators, and users all together, so that will gradually become able to speak many languages. A few packages already provide translations for their messages. If you found this `ABOUT-NLS' file inside a distribution, you may assume that the distributed package does use GNU `gettext' internally, itself available at your nearest GNU archive site. But you do _not_ need to install GNU `gettext' prior to configuring, installing or using this package with messages translated. Installers will find here some useful hints. These notes also explain how users should proceed for getting the programs to use the available translations. They tell how people wanting to contribute and work at translations should contact the appropriate team. When reporting bugs in the `intl/' directory or bugs which may be related to internationalization, you should tell about the version of `gettext' which is used. The information can be found in the `intl/VERSION' file, in internationalized packages. Quick configuration advice ========================== If you want to exploit the full power of internationalization, you should configure it using ./configure --with-included-gettext to force usage of internationalizing routines provided within this package, despite the existence of internationalizing capabilities in the operating system where this package is being installed. So far, only the `gettext' implementation in the GNU C library version 2 provides as many features (such as locale alias, message inheritance, automatic charset conversion or plural form handling) as the implementation here. It is also not possible to offer this additional functionality on top of a `catgets' implementation. Future versions of GNU `gettext' will very likely convey even more functionality. So it might be a good idea to change to GNU `gettext' as soon as possible. So you need _not_ provide this option if you are using GNU libc 2 or you have installed a recent copy of the GNU gettext package with the included `libintl'. INSTALL Matters =============== Some packages are "localizable" when properly installed; the programs they contain can be made to speak your own native language. Most such packages use GNU `gettext'. Other packages have their own ways to internationalization, predating GNU `gettext'. By default, this package will be installed to allow translation of messages. It will automatically detect whether the system already provides the GNU `gettext' functions. If not, the GNU `gettext' own library will be used. This library is wholly contained within this package, usually in the `intl/' subdirectory, so prior installation of the GNU `gettext' package is _not_ required. Installers may use special options at configuration time for changing the default behaviour. The commands: ./configure --with-included-gettext ./configure --disable-nls will respectively bypass any pre-existing `gettext' to use the internationalizing routines provided within this package, or else, _totally_ disable translation of messages. When you already have GNU `gettext' installed on your system and run configure without an option for your new package, `configure' will probably detect the previously built and installed `libintl.a' file and will decide to use this. This might be not what is desirable. You should use the more recent version of the GNU `gettext' library. I.e. if the file `intl/VERSION' shows that the library which comes with this package is more recent, you should use ./configure --with-included-gettext to prevent auto-detection. The configuration process will not test for the `catgets' function and therefore it will not be used. The reason is that even an emulation of `gettext' on top of `catgets' could not provide all the extensions of the GNU `gettext' library. Internationalized packages have usually many `po/LL.po' files, where LL gives an ISO 639 two-letter code identifying the language. Unless translations have been forbidden at `configure' time by using the `--disable-nls' switch, all available translations are installed together with the package. However, the environment variable `LINGUAS' may be set, prior to configuration, to limit the installed set. `LINGUAS' should then contain a space separated list of two-letter codes, stating which languages are allowed. Using This Package ================== As a user, if your language has been installed for this package, you only have to set the `LANG' environment variable to the appropriate `LL_CC' combination. Here `LL' is an ISO 639 two-letter language code, and `CC' is an ISO 3166 two-letter country code. For example, let's suppose that you speak German and live in Germany. At the shell prompt, merely execute `setenv LANG de_DE' (in `csh'), `export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash'). This can be done from your `.login' or `.profile' file, once and for all. You might think that the country code specification is redundant. But in fact, some languages have dialects in different countries. For example, `de_AT' is used for Austria, and `pt_BR' for Brazil. The country code serves to distinguish the dialects. The locale naming convention of `LL_CC', with `LL' denoting the language and `CC' denoting the country, is the one use on systems based on GNU libc. On other systems, some variations of this scheme are used, such as `LL' or `LL_CC.ENCODING'. You can get the list of locales supported by your system for your country by running the command `locale -a | grep '^LL''. Not all programs have translations for all languages. By default, an English message is shown in place of a nonexistent translation. If you understand other languages, you can set up a priority list of languages. This is done through a different environment variable, called `LANGUAGE'. GNU `gettext' gives preference to `LANGUAGE' over `LANG' for the purpose of message handling, but you still need to have `LANG' set to the primary language; this is required by other parts of the system libraries. For example, some Swedish users who would rather read translations in German than English for when Swedish is not available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'. In the `LANGUAGE' environment variable, but not in the `LANG' environment variable, `LL_CC' combinations can be abbreviated as `LL' to denote the language's main dialect. For example, `de' is equivalent to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT' (Portuguese as spoken in Portugal) in this context. Translating Teams ================= For the Free Translation Project to be a success, we need interested people who like their own language and write it well, and who are also able to synergize with other translators speaking the same language. Each translation team has its own mailing list. The up-to-date list of teams can be found at the Free Translation Project's homepage, `http://www.iro.umontreal.ca/contrib/po/HTML/', in the "National teams" area. If you'd like to volunteer to _work_ at translating messages, you should become a member of the translating team for your own language. The subscribing address is _not_ the same as the list itself, it has `-request' appended. For example, speakers of Swedish can send a message to `sv-request@li.org', having this message body: subscribe Keep in mind that team members are expected to participate _actively_ in translations, or at solving translational difficulties, rather than merely lurking around. If your team does not exist yet and you want to start one, or if you are unsure about what to do or how to get started, please write to `translation@iro.umontreal.ca' to reach the coordinator for all translator teams. The English team is special. It works at improving and uniformizing the terminology in use. Proven linguistic skill are praised more than programming skill, here. Available Packages ================== Languages are not equally supported in all packages. The following matrix shows the current state of internationalization, as of December 2003. The matrix shows, in regard of each package, for which languages PO files have been submitted to translation coordination, with a translation percentage of at least 50%. Ready PO files am az be bg ca cs da de el en en_GB eo es +-------------------------------------------+ a2ps | [] [] [] [] | aegis | () | ant-phone | () | anubis | | ap-utils | | bash | [] [] [] [] | batchelor | | bfd | [] [] | binutils | [] [] | bison | [] [] [] | bluez-pin | [] [] | clisp | | clisp | [] [] [] | coreutils | [] [] [] [] | cpio | [] [] [] | darkstat | [] () [] | diffutils | [] [] [] [] [] [] [] | e2fsprogs | [] [] | enscript | [] [] [] [] | error | [] [] [] [] [] | fetchmail | [] () [] [] [] [] | fileutils | [] [] [] | findutils | [] [] [] [] [] [] [] | flex | [] [] [] [] | fslint | | gas | [] | gawk | [] [] [] [] | gbiff | [] | gcal | [] | gcc | [] [] | gettext | [] [] [] [] [] | gettext-examples | [] [] [] | gettext-runtime | [] [] [] [] [] | gettext-tools | [] [] [] | gimp-print | [] [] [] [] [] | gliv | | glunarclock | [] [] | gnubiff | [] | gnucash | [] () [] [] | gnucash-glossary | [] () [] | gnupg | [] () [] [] [] [] | gpe-aerial | [] | gpe-beam | [] [] | gpe-calendar | [] [] | gpe-clock | [] [] | gpe-conf | [] [] | gpe-contacts | [] [] | gpe-edit | [] | gpe-go | [] | gpe-login | [] [] | gpe-ownerinfo | [] [] | gpe-sketchbook | [] [] | gpe-su | [] [] | gpe-taskmanager | [] [] | gpe-timesheet | [] | gpe-today | [] [] | gpe-todo | [] [] | gphoto2 | [] [] [] [] | gprof | [] [] [] | gpsdrive | () () () | gramadoir | [] | grep | [] [] [] [] [] [] | gretl | [] | gtick | () | hello | [] [] [] [] [] [] | id-utils | [] [] | indent | [] [] [] [] | jpilot | [] [] [] | jtag | | jwhois | [] | kbd | [] [] [] [] [] | latrine | () | ld | [] [] | libc | [] [] [] [] [] [] | libgpewidget | [] [] | libiconv | [] [] [] [] [] | lifelines | [] () | lilypond | [] | lingoteach | | lingoteach_lessons | () () | lynx | [] [] [] [] | m4 | [] [] [] [] | mailutils | [] [] | make | [] [] [] | man-db | [] () [] [] () | minicom | [] [] [] | mysecretdiary | [] [] [] | nano | [] () [] [] [] | nano_1_0 | [] () [] [] [] | opcodes | [] | parted | [] [] [] [] [] | ptx | [] [] [] [] [] | python | | radius | [] | recode | [] [] [] [] [] [] [] | rpm | [] [] | screem | | scrollkeeper | [] [] [] [] [] [] | sed | [] [] [] [] [] | sh-utils | [] [] [] | shared-mime-info | | sharutils | [] [] [] [] [] [] | silky | () | skencil | [] () [] | sketch | [] () [] | soundtracker | [] [] [] | sp | [] | tar | [] [] [] [] | texinfo | [] [] [] | textutils | [] [] [] [] | tin | () () | tuxpaint | [] [] [] [] [] [] [] | util-linux | [] [] [] [] [] | vorbis-tools | [] [] [] [] | wastesedge | () | wdiff | [] [] [] [] | wget | [] [] [] [] [] [] | xchat | [] [] [] [] | xfree86_xkb_xml | [] | xpad | [] | +-------------------------------------------+ am az be bg ca cs da de el en en_GB eo es 0 0 8 3 37 38 56 73 15 1 5 12 64 et fa fi fr ga gl he hr hu id is it ja +----------------------------------------+ a2ps | [] [] [] () | aegis | | ant-phone | | anubis | [] | ap-utils | [] | bash | [] [] | batchelor | [] | bfd | [] | binutils | [] [] | bison | [] [] [] [] | bluez-pin | [] [] [] [] [] | clisp | | clisp | [] | coreutils | [] [] [] [] [] [] | cpio | [] [] [] | darkstat | () [] [] [] | diffutils | [] [] [] [] [] [] [] | e2fsprogs | | enscript | [] [] | error | [] [] [] [] | fetchmail | [] | fileutils | [] [] [] [] [] [] | findutils | [] [] [] [] [] [] [] [] [] [] | flex | [] [] | fslint | | gas | [] | gawk | [] [] [] | gbiff | | gcal | [] | gcc | [] | gettext | [] [] | gettext-examples | [] [] | gettext-runtime | [] [] [] [] | gettext-tools | [] [] | gimp-print | [] [] | gliv | () | glunarclock | [] [] [] [] | gnubiff | | gnucash | () [] | gnucash-glossary | [] | gnupg | [] [] [] [] [] [] [] | gpe-aerial | [] | gpe-beam | [] | gpe-calendar | [] [] [] | gpe-clock | [] | gpe-conf | [] | gpe-contacts | [] [] | gpe-edit | [] [] | gpe-go | [] | gpe-login | [] [] | gpe-ownerinfo | [] [] [] | gpe-sketchbook | [] | gpe-su | [] | gpe-taskmanager | [] | gpe-timesheet | [] [] [] | gpe-today | [] [] | gpe-todo | [] [] | gphoto2 | [] [] [] | gprof | [] [] | gpsdrive | () [] () () | gramadoir | [] | grep | [] [] [] [] [] [] [] [] [] [] [] | gretl | [] | gtick | [] [] | hello | [] [] [] [] [] [] [] [] [] [] [] [] | id-utils | [] [] [] [] | indent | [] [] [] [] [] [] [] [] [] | jpilot | [] () | jtag | | jwhois | [] [] [] [] | kbd | [] | latrine | | ld | [] | libc | [] [] [] [] [] | libgpewidget | [] [] [] [] | libiconv | [] [] [] [] [] [] [] [] [] | lifelines | () | lilypond | [] | lingoteach | [] [] | lingoteach_lessons | | lynx | [] [] [] [] | m4 | [] [] [] [] | mailutils | | make | [] [] [] [] [] | man-db | () () | minicom | [] [] [] [] | mysecretdiary | [] [] | nano | [] [] [] [] | nano_1_0 | [] [] [] [] | opcodes | [] | parted | [] [] [] | ptx | [] [] [] [] [] [] [] | python | | radius | [] | recode | [] [] [] [] [] [] | rpm | | screem | | scrollkeeper | [] | sed | [] [] [] [] [] [] [] [] [] | sh-utils | [] [] [] [] [] [] [] | shared-mime-info | [] | sharutils | [] [] [] [] [] | silky | [] () | skencil | [] | sketch | [] | soundtracker | [] [] [] [] | sp | [] () | tar | [] [] [] [] [] [] [] [] [] | texinfo | [] [] [] [] | textutils | [] [] [] [] [] | tin | [] () | tuxpaint | [] [] [] [] [] [] [] [] | util-linux | [] [] [] [] () [] | vorbis-tools | [] | wastesedge | () | wdiff | [] [] [] [] [] [] | wget | [] [] [] [] [] [] [] | xchat | [] [] [] | xfree86_xkb_xml | | xpad | [] | +----------------------------------------+ et fa fi fr ga gl he hr hu id is it ja 21 1 25 86 24 24 8 10 38 31 1 23 32 ko lg lt lv ms nb nl nn no pl pt pt_BR ro +-------------------------------------------+ a2ps | () [] [] () () [] [] | aegis | () () | ant-phone | [] [] | anubis | [] [] [] [] [] | ap-utils | [] () [] | bash | [] [] | batchelor | [] | bfd | [] | binutils | | bison | [] [] [] [] | bluez-pin | [] [] [] | clisp | | clisp | [] | coreutils | [] | cpio | [] [] [] [] [] | darkstat | [] [] [] [] | diffutils | [] [] [] [] | e2fsprogs | [] | enscript | [] [] [] | error | [] [] [] | fetchmail | [] [] () | fileutils | [] [] | findutils | [] [] [] [] [] | flex | [] [] [] [] | fslint | [] [] | gas | | gawk | [] [] [] | gbiff | [] [] | gcal | | gcc | | gettext | [] [] [] | gettext-examples | [] [] | gettext-runtime | [] [] [] | gettext-tools | [] [] [] | gimp-print | [] | gliv | [] [] [] | glunarclock | [] [] [] | gnubiff | | gnucash | [] [] () | gnucash-glossary | [] [] | gnupg | [] | gpe-aerial | [] [] [] | gpe-beam | [] [] [] | gpe-calendar | [] [] [] | gpe-clock | [] [] [] | gpe-conf | [] [] [] | gpe-contacts | [] [] [] | gpe-edit | [] [] [] | gpe-go | [] [] | gpe-login | [] [] [] | gpe-ownerinfo | [] [] [] | gpe-sketchbook | [] [] [] | gpe-su | [] [] [] | gpe-taskmanager | [] [] [] | gpe-timesheet | [] [] [] | gpe-today | [] [] [] | gpe-todo | [] [] [] | gphoto2 | [] | gprof | [] [] | gpsdrive | () () () [] | gramadoir | [] | grep | [] [] [] [] | gretl | | gtick | [] [] | hello | [] [] [] [] [] [] [] [] [] [] | id-utils | [] [] [] | indent | [] [] [] | jpilot | () () | jtag | | jwhois | [] [] [] [] | kbd | [] [] [] | latrine | [] | ld | | libc | [] [] [] [] [] | libgpewidget | [] [] [] | libiconv | [] [] [] [] | lifelines | | lilypond | | lingoteach | | lingoteach_lessons | | lynx | [] [] | m4 | [] [] [] [] | mailutils | [] [] | make | [] [] [] [] | man-db | [] | minicom | [] [] [] | mysecretdiary | [] [] [] | nano | [] [] [] [] | nano_1_0 | [] [] [] [] [] | opcodes | [] [] | parted | [] [] [] [] | ptx | [] [] [] [] [] [] [] | python | | radius | [] | recode | [] [] [] | rpm | [] [] | screem | | scrollkeeper | [] [] [] [] | sed | [] [] [] | sh-utils | [] | shared-mime-info | [] | sharutils | [] | silky | | skencil | [] [] | sketch | [] [] | soundtracker | | sp | | tar | [] [] [] [] [] [] | texinfo | [] [] [] | textutils | [] [] | tin | | tuxpaint | [] [] [] [] [] [] [] [] [] | util-linux | [] [] | vorbis-tools | [] [] | wastesedge | | wdiff | [] [] [] [] | wget | [] [] | xchat | [] [] | xfree86_xkb_xml | [] | xpad | [] [] | +-------------------------------------------+ ko lg lt lv ms nb nl nn no pl pt pt_BR ro 12 0 1 2 12 10 60 4 4 38 25 35 76 ru sk sl sr sv ta tr uk vi wa zh_CN zh_TW +-------------------------------------------+ a2ps | [] [] [] [] [] | 16 aegis | () | 0 ant-phone | | 2 anubis | [] [] [] | 9 ap-utils | () | 3 bash | [] | 9 batchelor | | 2 bfd | [] [] | 6 binutils | [] [] [] | 7 bison | [] [] [] | 14 bluez-pin | [] [] [] | 13 clisp | | 0 clisp | | 5 coreutils | [] [] [] [] [] | 16 cpio | [] [] [] | 14 darkstat | [] [] [] () () | 12 diffutils | [] [] [] [] | 22 e2fsprogs | [] [] | 5 enscript | [] [] [] | 12 error | [] [] [] | 15 fetchmail | [] [] [] | 11 fileutils | [] [] [] [] [] [] | 17 findutils | [] [] [] [] [] [] [] | 29 flex | [] [] [] | 13 fslint | | 2 gas | [] | 3 gawk | [] [] | 12 gbiff | | 3 gcal | [] [] | 4 gcc | [] | 4 gettext | [] [] [] [] [] [] | 16 gettext-examples | [] [] [] [] | 11 gettext-runtime | [] [] [] [] [] [] [] [] [] | 21 gettext-tools | [] [] [] [] [] [] | 14 gimp-print | [] [] | 10 gliv | | 3 glunarclock | [] [] [] [] | 13 gnubiff | | 1 gnucash | [] [] [] | 9 gnucash-glossary | [] [] [] | 8 gnupg | [] [] [] [] | 17 gpe-aerial | [] [] | 7 gpe-beam | [] [] | 8 gpe-calendar | [] [] [] [] [] | 13 gpe-clock | [] [] [] [] | 10 gpe-conf | [] [] [] | 9 gpe-contacts | [] [] [] [] | 11 gpe-edit | [] [] [] [] [] [] | 12 gpe-go | [] | 5 gpe-login | [] [] [] [] [] [] | 13 gpe-ownerinfo | [] [] [] [] [] | 13 gpe-sketchbook | [] [] [] | 9 gpe-su | [] [] [] [] | 10 gpe-taskmanager | [] [] [] [] | 10 gpe-timesheet | [] [] [] [] [] | 12 gpe-today | [] [] [] [] [] [] | 13 gpe-todo | [] [] [] [] [] | 12 gphoto2 | [] [] [] | 11 gprof | [] [] | 9 gpsdrive | [] [] | 4 gramadoir | | 3 grep | [] [] [] [] [] | 26 gretl | | 2 gtick | [] | 5 hello | [] [] [] [] [] | 33 id-utils | [] [] [] | 12 indent | [] [] [] [] [] | 21 jpilot | [] [] [] [] [] | 9 jtag | [] | 1 jwhois | () () [] [] | 11 kbd | [] [] | 11 latrine | | 1 ld | [] [] | 5 libc | [] [] [] [] | 20 libgpewidget | [] [] [] [] | 13 libiconv | [] [] [] [] [] [] [] [] [] | 27 lifelines | [] | 2 lilypond | [] | 3 lingoteach | | 2 lingoteach_lessons | () | 0 lynx | [] [] [] [] | 14 m4 | [] [] [] | 15 mailutils | [] | 5 make | [] [] [] [] | 16 man-db | [] | 5 minicom | [] | 11 mysecretdiary | [] [] | 10 nano | [] [] [] [] [] | 17 nano_1_0 | [] [] [] [] | 17 opcodes | [] [] | 6 parted | [] [] [] | 15 ptx | [] [] [] | 22 python | | 0 radius | [] | 4 recode | [] [] [] [] | 20 rpm | [] [] [] | 7 screem | [] [] | 2 scrollkeeper | [] [] [] [] | 15 sed | [] [] [] [] [] [] | 23 sh-utils | [] [] [] | 14 shared-mime-info | [] [] | 4 sharutils | [] [] [] [] [] | 17 silky | () | 2 skencil | [] | 6 sketch | [] | 6 soundtracker | [] [] | 9 sp | [] | 3 tar | [] [] [] [] [] | 24 texinfo | [] [] [] [] | 14 textutils | [] [] [] [] [] | 16 tin | | 1 tuxpaint | [] [] [] [] [] | 29 util-linux | [] [] [] | 15 vorbis-tools | [] | 8 wastesedge | | 0 wdiff | [] [] [] [] | 18 wget | [] [] [] [] [] [] [] [] | 23 xchat | [] [] [] [] [] | 14 xfree86_xkb_xml | [] [] [] [] [] [] | 8 xpad | | 4 +-------------------------------------------+ 51 teams ru sk sl sr sv ta tr uk vi wa zh_CN zh_TW 120 domains 59 42 16 25 81 0 56 12 1 10 21 22 1260 Some counters in the preceding matrix are higher than the number of visible blocks let us expect. This is because a few extra PO files are used for implementing regional variants of languages, or language dialects. For a PO file in the matrix above to be effective, the package to which it applies should also have been internationalized and distributed as such by its maintainer. There might be an observable lag between the mere existence a PO file and its wide availability in a distribution. If December 2003 seems to be old, you may fetch a more recent copy of this `ABOUT-NLS' file on most GNU archive sites. The most up-to-date matrix with full percentage details can be found at `http://www.iro.umontreal.ca/contrib/po/HTML/matrix.html'. Using `gettext' in new packages =============================== If you are writing a freely available program and want to internationalize it you are welcome to use GNU `gettext' in your package. Of course you have to respect the GNU Library General Public License which covers the use of the GNU `gettext' library. This means in particular that even non-free programs can use `libintl' as a shared library, whereas only free software can use `libintl' as a static library or use modified versions of `libintl'. Once the sources are changed appropriately and the setup can handle the use of `gettext' the only thing missing are the translations. The Free Translation Project is also available for packages which are not developed inside the GNU project. Therefore the information given above applies also for every other Free Software Project. Contact `translation@iro.umontreal.ca' to make the `.pot' files available to the translation teams. libinstpatch-1.1.6/AUTHORS000066400000000000000000000013461400263525300152570ustar00rootroot00000000000000Element Green Contributors: BALATON Zoltan for Spectralis support, Mac OS X integration and MAC builds. Ebrahim Mayat for Mac OS X testing, support and build HOWTO documentation Keishi Suenaga for Win32 patches and build HOWTO documentation Credits: Information on the GigaSampler format (no code used though) ----------------------------------------------------------- libgig - C++ cross-platform Gigasampler format file loader library Copyright (C) 2003-2005 by Christian Schoenebeck LinuxSampler - modular, streaming capable sampler Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck Copyright (C) 2005 Christian Schoenebeck libinstpatch-1.1.6/CMakeLists.txt000066400000000000000000000222231400263525300167440ustar00rootroot00000000000000# # libInstPatch # # Copyright (C) 1999-2014 Element Green # # See COPYING license file for distribution details # project ( libInstPatch C ) cmake_minimum_required ( VERSION 3.0 ) set ( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ) # libInstPatch package name set ( PACKAGE "libinstpatch" ) # libInstPatch package version set ( IPATCH_VERSION_MAJOR 1 ) set ( IPATCH_VERSION_MINOR 1 ) set ( IPATCH_VERSION_MICRO 6 ) set ( VERSION "${IPATCH_VERSION_MAJOR}.${IPATCH_VERSION_MINOR}.${IPATCH_VERSION_MICRO}" ) set ( IPATCH_VERSION "\"${VERSION}\"" ) # libinstpatch - Library version # *** NOTICE *** # Update library version upon each release (follow these steps in order) # if any source code changes: REVISION++ # if any interfaces added/removed/changed: REVISION=0 # if any interfaces removed/changed (compatibility broken): CURRENT++ # if any interfaces have been added: AGE++ # if any interfaces have been removed/changed (compatibility broken): AGE=0 # This is not exactly the same algorithm as the libtool one, but the results are the same. set ( LIB_VERSION_CURRENT 2 ) set ( LIB_VERSION_AGE 2 ) set ( LIB_VERSION_REVISION 0 ) set ( LIB_VERSION_INFO "${LIB_VERSION_CURRENT}.${LIB_VERSION_AGE}.${LIB_VERSION_REVISION}" ) # Options disabled by default option ( enable-debug "enable debugging (default=no)" off ) option ( GTKDOC_ENABLED "Create Gtk-Doc API reference (default=no)" off ) option ( INTROSPECTION_ENABLED "Create GObject Introspection typelib" off ) # Options enabled by default option ( BUILD_SHARED_LIBS "Build a shared object or DLL" on ) # Initialize the library directory name suffix. if ( CMAKE_SIZEOF_VOID_P EQUAL 8 ) set ( _init_lib_suffix "64" ) else ( CMAKE_SIZEOF_VOID_P EQUAL 8 ) set ( _init_lib_suffix "" ) endif ( CMAKE_SIZEOF_VOID_P EQUAL 8 ) set ( LIB_SUFFIX ${_init_lib_suffix} CACHE STRING "library directory name suffix (32/64/nothing)" ) mark_as_advanced ( LIB_SUFFIX ) # Default install directory names include ( DefaultDirs ) # Basic C library checks include ( CheckSTDC ) include ( CheckIncludeFile ) check_include_file ( string.h HAVE_STRING_H ) check_include_file ( stdlib.h HAVE_STDLIB_H ) check_include_file ( stdio.h HAVE_STDIO_H ) check_include_file ( math.h HAVE_MATH_H ) check_include_file ( errno.h HAVE_ERRNO_H ) check_include_file ( stdarg.h HAVE_STDARG_H ) check_include_file ( unistd.h HAVE_UNISTD_H ) check_include_file ( locale.h HAVE_LOCALE_H ) check_include_file ( xlocale.h HAVE_XLOCALE_H ) if ( WIN32 ) # Check presence of MS include files check_include_file ( io.h HAVE_IO_H ) endif( WIN32 ) unset ( IPATCH_CPPFLAGS CACHE ) unset ( IPATCH_LIBS CACHE ) # Options for the GNU C compiler only if ( CMAKE_COMPILER_IS_GNUCC ) if ( NOT APPLE ) set ( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed" ) set ( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined" ) endif ( NOT APPLE ) set ( GNUCC_WARNING_FLAGS "-Wall") set ( CMAKE_C_FLAGS "-pedantic ${CMAKE_C_FLAGS}" ) set ( CMAKE_C_FLAGS_DEBUG "-g -DDEBUG -fsanitize=undefined ${GNUCC_WARNING_FLAGS} ${CMAKE_C_FLAGS_DEBUG}" ) set ( CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG ${GNUCC_WARNING_FLAGS} ${CMAKE_C_FLAGS_RELEASE}" ) set ( CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG ${GNUCC_WARNING_FLAGS} ${CMAKE_C_FLAGS_RELWITHDEBINFO}" ) endif ( CMAKE_COMPILER_IS_GNUCC ) if ( MSVC ) # statically link in the CRT library to avoid a bunch of runtime DLL dependencies and allow # the CI windows builds to be run under WinXP foreach ( flag_var CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO ) if ( ${flag_var} MATCHES "/MD" ) string ( REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}" ) endif ( ${flag_var} MATCHES "/MD" ) endforeach ( flag_var ) endif ( MSVC ) if ( enable-debug ) set ( CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the build type, options: Debug Release RelWithDebInfo" FORCE ) set ( IPATCH_DEBUG 1 ) endif ( enable-debug ) unset ( MINGW32 CACHE ) if ( WIN32 ) # MinGW compiler (a Windows GCC port) if ( MINGW ) set ( MINGW32 1 ) add_definitions ( -mms-bitfields ) endif ( MINGW ) else ( WIN32 ) set ( IPATCH_LIBS "m" ) endif ( WIN32 ) unset ( ENABLE_DEBUG CACHE ) unset ( DEBUG CACHE ) if ( CMAKE_BUILD_TYPE MATCHES "Debug" ) set ( ENABLE_DEBUG 1 ) set ( DEBUG 1 ) endif ( CMAKE_BUILD_TYPE MATCHES "Debug" ) # Mandatory tool: pkg-config find_package ( PkgConfig REQUIRED ) # Mandatory libraries: gobject, glib and gthread pkg_check_modules ( GOBJECT REQUIRED gobject-2.0>=2.12 glib-2.0>=2.12 gthread-2.0>=2.12 ) # Disable deprecation warnings for now (fixed in master) add_definitions ( -DGLIB_DISABLE_DEPRECATION_WARNINGS ) include ( UnsetPkgConfig ) # Check for libsndfile pkg_check_modules ( SNDFILE REQUIRED sndfile>=1.0.0 ) # Check for GObjectIntrospection binding if (INTROSPECTION_ENABLED) include (FindGObjectIntrospection) endif () # Check for Gtk-Doc if (GTKDOC_ENABLED) find_package(GtkDoc) endif () # General configuration file configure_file ( ${CMAKE_SOURCE_DIR}/config.h.cmake ${CMAKE_BINARY_DIR}/config.h ) add_definitions ( -DHAVE_CONFIG_H ) # Version and master libinstpatch.h file configure_file ( ${CMAKE_SOURCE_DIR}/libinstpatch/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/libinstpatch/version.h ) configure_file ( ${CMAKE_SOURCE_DIR}/libinstpatch/libinstpatch.h.in ${CMAKE_CURRENT_BINARY_DIR}/libinstpatch/libinstpatch.h ) set (INSTPATCH_INSTALL_TARGET "libinstpatch-${LIB_VERSION_CURRENT}") # pkg-config support if ( UNIX OR MINGW OR WIN32) set ( prefix "${CMAKE_INSTALL_PREFIX}" ) set ( exec_prefix "\${prefix}" ) if ( IS_ABSOLUTE "${LIB_INSTALL_DIR}" ) set ( libdir "${LIB_INSTALL_DIR}" ) else () set ( libdir "\${exec_prefix}/${LIB_INSTALL_DIR}" ) endif () if ( IS_ABSOLUTE "${INCLUDE_INSTALL_DIR}" ) set ( includedir "${INCLUDE_INSTALL_DIR}/${INSTPATCH_INSTALL_TARGET}" ) else () set ( includedir "\${prefix}/${INCLUDE_INSTALL_DIR}/${INSTPATCH_INSTALL_TARGET}" ) endif () # stamp library name with version current value (for Windows only) if(MINGW OR WIN32) set ( lib_version_suffix ${LIB_VERSION_CURRENT} ) else(MINGW OR WIN32) set ( lib_version_suffix 1.0 ) endif(MINGW OR WIN32) configure_file ( libinstpatch-1.0.pc.in ${CMAKE_BINARY_DIR}/libinstpatch-1.0.pc IMMEDIATE @ONLY ) install ( FILES ${CMAKE_BINARY_DIR}/libinstpatch-1.0.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig ) endif ( UNIX OR MINGW OR WIN32) # Extra targets for Unix build environments if ( UNIX ) # uninstall custom target configure_file ( "${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target ( uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") endif ( UNIX ) # Process subdirectories add_subdirectory ( libinstpatch ) add_subdirectory ( utils ) add_subdirectory ( docs ) # CPack support set ( CPACK_PACKAGE_DESCRIPTION_SUMMARY "libInstPatch instrument editing library" ) set ( CPACK_PACKAGE_VENDOR "swami.sourceforge.net" ) set ( CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README.md" ) set ( CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/COPYING" ) set ( CPACK_PACKAGE_VERSION_MAJOR ${IPATCH_VERSION_MAJOR} ) set ( CPACK_PACKAGE_VERSION_MINOR ${IPATCH_VERSION_MINOR} ) set ( CPACK_PACKAGE_VERSION_PATCH ${IPATCH_VERSION_MICRO} ) # source packages set ( CPACK_SOURCE_GENERATOR TGZ;TBZ2;ZIP ) set ( CPACK_SOURCE_IGNORE_FILES "/.svn/;~$;.cproject;.project;/.settings/;${CPACK_SOURCE_IGNORE_FILES}" ) set ( CPACK_SOURCE_PACKAGE_FILE_NAME "${PACKAGE}-${VERSION}" ) set ( CPACK_SOURCE_STRIP_FILES OFF ) # binary packages include ( InstallRequiredSystemLibraries ) set ( CPACK_GENERATOR STGZ;TGZ;TBZ2;ZIP ) set ( CPACK_PACKAGE_NAME ${PACKAGE} ) set ( CPACK_STRIP_FILES ON ) include ( CPack ) message ( "Build options:" ) if ( ENABLE_DEBUG ) message ( "Debug: yes" ) else ( ENABLE_DEBUG ) message ( "Debug: no" ) endif ( ENABLE_DEBUG ) if (INTROSPECTION_FOUND) message ( "GObjectIntrospection: yes" ) else (INTROSPECTION_FOUND) message ( "GObjectIntrospection: no" ) endif(INTROSPECTION_FOUND) if (GTKDOC_FOUND) message ( "Gtk-Doc API reference: yes" ) else (GTKDOC_FOUND) message ( "Gtk-Doc API reference: no" ) endif(GTKDOC_FOUND) file(GLOB_RECURSE ALL_SOURCE_FILES LIST_DIRECTORIES false ${CMAKE_SOURCE_DIR}/*.[chi] ${CMAKE_SOURCE_DIR}/*.[chi]pp ${CMAKE_SOURCE_DIR}/*.[chi]xx ${CMAKE_SOURCE_DIR}/*.cc ${CMAKE_SOURCE_DIR}/*.hh ${CMAKE_SOURCE_DIR}/*.ii ${CMAKE_SOURCE_DIR}/*.[CHI] ) find_program ( ASTYLE "astyle" ) if ( ASTYLE ) add_custom_target( format COMMAND ${ASTYLE} -A1 -xb -j -k3 -p -f -n -U ${ALL_SOURCE_FILES} ) endif ( ASTYLE) libinstpatch-1.1.6/COPYING000066400000000000000000000577501400263525300152540ustar00rootroot00000000000000NOTE: This software is restricted to version 2.1 of the LGPL only. GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS libinstpatch-1.1.6/ChangeLog000066400000000000000000001432441400263525300157650ustar00rootroot000000000000002008-12-14 Josh Green * libinstpatch/IpatchContainer.c: Updated unpickler callback. * libinstpatch/IpatchPickler.c (ipatch_pickler_default_object_encode_func): Added missing parameter in call to ipatch_pickler_encode_property(). * libinstpatch/IpatchRange.c: Updated unpickler callback. * libinstpatch/IpatchUnpickler.h: Removed some stuff which was causing conflicts with the rest of the pickle headers. Code not yet complete. 2008-12-07 Josh Green * libinstpatch/IpatchPickle.h: Now using IpatchUnpickler (not yet complete). * libinstpatch/IpatchPickler.c: Added create_element parameter to indicate if a surrounding should be created for functions ipatch_pickler_encode_object() ipatch_pickler_encode_property() and ipatch_pickler_encode_property_by_name(). * libinstpatch/IpatchUnpickler.[ch]: New files. Initial unpickler, not yet complete. 2008-08-19 Josh Green * configure.ac: Bumped required glib/gobject version to 2.10. * libinstpatch/IpatchRange.c: Added GValue comparison function. * libinstpatch/IpatchSF2Gen.c (ipatch_sf2_gen_range_intersect): Fixed bug in return value statement. * libinstpatch/IpatchSF2Preset.c: Now doing proper IpatchItem notifies for bank and percussion flag. * libinstpatch/IpatchStrArray.c: New string array boxed type. * libinstpatch/IpatchTypeProp.c: Removed compatibility code with older versions of glib/gobject. * libinstpatch/misc.c: Removed compatibility code with older versions of glib/gobject. * python/ipatch.defs: Updates to Python binding. 2008-03-30 Josh Green * Lots of documentation updates: removed file descriptions from .c files, added gtk-doc file descriptions to .h files and updated Copyright to 1999-2008. * Updated many depricated uses of gobject functions g_value_set_string_take_ownership and g_value_set_boxed_take_ownership. * autogen.sh: Now calling gtkdocize. * configure.ac: GTK_DOC_CHECK is now 1.9. * IpatchConverter.c (ipatch_converter_log_printf): New function to do printf style logging to converter log. * IpatchItem.c (ipatch_item_set_property_override): Added a hack to GObject property set override to handle interface properties, renamed ipatch_item_get_ancestor_type to ipatch_item_get_ancestor_by_type and ipatch_item_peek_ancestor_type to ipatch_item_peek_ancestor_by_type * IpatchSF2Mod.c (ipatch_sf2_mod_list_duplicate): Now reverses the duplicated modulator list, instead of leaving it backwards from original. * IpatchSF2VoiceCache.c (ipatch_sf2_voice_copy): New function to copy a voice structure. * IpatchSF2Writer.c (sfont_write_pgens): Fixed crash bug in relation to switchover to global zone generators now in preset object. Thanks to Frédéric Fournier for pointing this out. * IpatchVBank.[ch]: New virtual bank object. * IpatchVBankInst.[ch]: New virtual bank instrument object. * IpatchVBankRegion.[ch]: New virtual bank region object. * IpatchSF2VoiceCache_VBank.[ch]: New virtual bank SF2 synthesis objects. * python/ipatch.defs: Updated Python binding. 2007-12-15 Josh Green * libinstpatch/IpatchParamProp.h: Added IPATCH_PARAM_NO_SAVE flag to indicate that an object property should not be saved to XML for the object's state. * libinstpatch/IpatchPickle.[ch]: Now contains only the pickle XML encoder/decoder function registration system. * libinstpatch/IpatchPickler.[ch]: New object instance which handles encoding objects/properties/values to XML. * libinstpatch/IpatchContainer.c: Added XML pickle encoder handler for container object types. * libinstpatch/IpatchBase.c: Marked some properties with IPATCH_PARAM_NO_SAVE. * libinstpatch/IpatchItem.c: Marked some properties with IPATCH_PARAM_NO_SAVE. * libinstpatch/IpatchRange.c: Added XML pickle encoder/decoder handlers for IpatchRange boxed type. 2007-11-26 Josh Green * docs/reference/libinstpatch-docs.sgml: Added IpatchSF2GenItem page to API reference index. * libinstpatch/IpatchSF2Gen.[ch]: Split IpatchSF2GenItem interface into separate files. * libinstpatch/IpatchSF2GenItem.[ch]: New home of IpatchSF2GenItem interface. * libinstpatch/IpatchSF2Mod.[ch]: Split IpatchSF2ModItem interface into separate files. * libinstpatch/IpatchSF2ModItem.[ch]: New home of IpatchSF2ModItem interface. * python/ipatch.defs: Fixed declaration of interfaces (IpatchSF2GenItem, IpatchSF2ModItem and IpatchSample) and other updates. 2007-05-10 Josh Green * libinstpatch/IpatchCram.h: Removed format specification from header, its now in an OpenOffice document in the trunk/media folder. * libinstpatch/IpatchTypeProp.c: Fixed bugs with GType related type properties "virtual-parent-type", "virtual-child-type" and "link-type". GType is defined as a gulong, which on 32 bit systems is the same size as a guint (what these properties were previously), on some 64 bit systems though, a gulong is 64 bits while a guint is 32, causing crashes when a corrupted GType lookup attempt is made. Added conditional for using new GType GParamSpecs in 2.10.0, with fallback to gulong. Thanks to Henry Kroll for reporting this. * libinstpatch/ipatch_priv.h: Added GTYPE_PARAM_SUPPORT macro to test for presence of GType param properties. 2007-05-09 Josh Green * libinstpatch/IpatchCramDecoder.[ch]: Added WavPack decoding support. * libinstpatch/IpatchCramEncoder.c: Fixed some bugs with encoder in regards to WavPack. Starting to add segment grouping and save as binary support, but not yet done. 2007-05-03 Josh Green * Switched license of libInstPatch to LGPL 2.1 and updated license text in all source files accordingly. Also updated copyright year. * python/ipatch.override: Added override for next_file() method of IpatchCramDecoder so that the decoder can be used fully in python. * libinstpatch/IpatchCramEncoder.c: Added WavPack support. All FLAC related properties are now prefixed with "flac-". (ipatch_cram_encoder_audio_params_new): Now takes an encoder instance so that the audio encoder type can be initialized from the "default-audio-encoder" property. * README: Updated README. * utils/cram.c: Added WavPack support with switch to choose between FLAC or WavPack. FLAC is still the default, since there is not yet decoding support for WavPack, and there may be other changes before the format is finalized. * libwavpack: Built in WavPack from WavPack 4.40.0. 2007-04-20 Josh Green * configure.ac: Added --disable-python configure option. 2007-04-19 Josh Green * utils/cram.c: Fixed -t test mode, NULL I/O functions were being assigned to the IpatchFile object but the file was not set to "open". Thanks to Dominique Würtz for reporting this problem. 2007-04-18 Josh Green * libinstpatch/misc.c: Removed BUILD_CRAM #ifdef, since CRAM is now always built and that define is no more, which was breaking CRAM converters (including cram utility). Thanks to Dominique Würtz for reporting that there was a problem with cram. 2007-04-06 Josh Green * libinstpatch/IpatchSample.[ch]: Added get_data and set_data methods to the IpatchSample interface for getting and setting sample data as an IpatchSampleData object. New functions ipatch_sample_get_data() ipatch_sample_set_data(), and ipatch_sample_has_data(). * libinstpatch/IpatchSF2Sample.c: Implimented new IpatchSample get_data and set_data methods. 2007-03-21 Josh Green * libinstpatch/IpatchPaste.c: Added ipatch_paste_get_add_list() function to retrieve the list of added paste objects. 2007-03-01 Josh Green * libinstpatch/sample.c: Added verification of channel and channel routing fields to ipatch_sample_format_verify(), fixed bug in ipatch_sample_get_transform_funcs() related to stereo to mono right channel audio conversion. * libinstpatch/ipatch_priv.h: Changed IPATCH_SAMPLE_TRANS_BUFFER_SIZE to 32k instead of 64k (a bit large) and now using for IpatchSampleTransform pool. * libinstpatch/IpatchSampleData.c: Changed ipatch_sample_data_new() to take the size of the sample data object as a parameter. * libinstpatch/IpatchSampleStoreVirtual.c: Changed functions to take IpatchSampleStore pointer instead, to be consistent with other store types. * libinstpatch/IpatchSampleTransform.c: Now using IPATCH_SAMPLE_TRANS_BUFFER_SIZE in ipatch_priv.h for sizes of transforms in pool. * libinstpatch/IpatchSampleStore.c: Added new ipatch_sample_store_new() for added convenience. Converted ipatch_sample_store_copy() to using sample transform pool. * libinstpatch/IpatchSampleList.c: Bug fixes in ipatch_sample_list_render() including a crasher and channel routing related bugs. Added ipatch_sample_list_dump() function for debugging. * libinstpatch/IpatchSampleList.h: Added IPATCH_SAMPLE_LIST_ITEM_GET_CHAN_ROUTE macro for getting channel routing from a sample list item. * libinstpatch/acinclude.m4: Added IPATCH_DEBUG config.h variable for building/including other debug code during compile time. * configure.ac: New sub dir tests/. * Makefile.am: New sub dir tests/. * tests/sample_test.c: Sample audio transform tests. * tests/sample_list_test.c: Sample edit list test. 2007-02-26 Josh Green * libinstpatch/sample.h: Increased the sample width field of the sample format integer to 4 bits to allow for 8 new backwards compatible widths/formats in the future, also increased channel field to 3 bits for future surround sound support (up to 8 channels), * libinstpatch/sample.c: Many many many bug fixes in regards to sample transform functions, thanks to the in progress test suit, modified ipatch_sample_get_transform_funcs() to not allocate but instead store to a caller supplied array with maximum length. * libinstpatch/IpatchSampleList.[ch]: New sample segment lists for doing sample edits, non-destructively. Not yet tested. * libinstpatch/IpatchSampleStoreVirtual.[ch]: Modified to use new IpatchSampleList structure. Not yet tested. * libinstpatch/IpatchSampleTransform.[ch]: Added sample transform pool functionality for fast retrieval of a sample transform object for temporary use without the over head of object creation, transform object should now be fixed in regards to changing format, removed ipatch_sample_transform_set_buffers() since we now enforce that buffers are allocated together to make format changes possible, modified ipatch_sample_transform_convert() to allow source and destination buffers to be specified of any size added ipatch_sample_transform_convert_single() with old style 1 call per buffer conversion. * libinstpatch/IpatchSampleStore.c: Modified ipatch_sample_store_transform_read() and ipatch_sample_store_transform_write() to take an optional buffer parameter which allows for reading/writing of larger sizes than the max frames of the transform object. * libinstpatch/IpatchAudioFile.c: Fixed bugs in regards to floating point audio and 24 bit and added transformations for real 24 bit 3 byte audio and new function ipatch_audio_file_new(). 2007-02-15 Josh Green * Modified build to include version in version sensitive files so that future major versions can be installed in parellel. * libinstpatch/IpatchState*.[ch]: Migrated from libswami, not yet being used, but will be the bases of undo sub system. * libinstpatch/IpatchSF2Preset.c: Removed ipatch_sf2_preset_has_global_zone() since IpatchSF2Preset is now the global zone. * libinstpatch/IpatchPickle.[ch]: XML <> Object pickling, not yet done and inactive. * libinstpatch/IpatchSF2Sample.c: Sample property flags are now being set on which ones affect synthesis and which ones should be real time, also added a sf2voice-update-func to handle real time effect changes. * libinstpatch/IpatchSF2VoiceCache_SF2.c: Removed disabled and outdated realtime generator effect code. Replacing with new generic system. * libinstpatch/misc.c: Removed "sort-children" type flag from all types except preset related ones. * libinstpatch/IpatchSF2VoiceCache.c: Added new ipatch_sf2_voice_cache_update() function for the new real time voice cache update functionality and a sf2voice-update-func type property to register handler functions to specific types. * TODO.tasks: Anjuta-2.1 tasks file for use with Anjuta dev, rocks! * libinstpatch.anjuta: Anjuta-2.1 project file. 2007-01-14 Josh Green * zlib is now required (no longer optional) so CRAM support is always available. Easier than trying to remove CRAM from Pythong binding and builtin_enums. Besides, zlib is rather standard. * acinclude.m4: Modified some of the builtin automake functions so that they don't produce the "underquoted definition" warnings. AM_PATH_ZLIB was defining a main() function with AC_TRY_COMPILE which it already does, causing a nested function which was failing on OSX. 2006-12-28 Josh Green * libinstpatch/IpatchContainer.c: Reverting change of type swap in ipatch_container_get_children() which was not a good idea. 2006-12-22 Josh Green * All IpatchItem "title" property notifies of all items now send at least the new value and sometimes also the old value (when convenient). * Most ipatch_item_changed() calls removed as it is now down automatically through the property notify system. * libinstpatch/IpatchItem.c: Removed item property hook function and replaced with more powerful item property notify system in IpatchItemProp.c, ipatch_item_changed() now implicitly called when an IpatchItem property notify occurs (IPATCH_PARAM_NO_SAVE_CHANGE property flag can be used to indicate that property is not part of saveable state), performance improved, and now doing notify for IpatchBase "changed" property. * libinstpatch/IpatchDLS2Param_tables.c: New file - Initial table data for DLS parameters, not yet completed. * libinstpatch/IpatchItemProp.c: New file - Item property notify system moved from Swami to libinstpatch and improved. * libinstpatch/IpatchContainer_notify.c: New file - Container add/remove notify system moved from Swami to libinstpatch and improved. * libinstpatch/IpatchSF2Inst.c: Converted to using IpatchSF2GenItem and IpatchSF2ModItem interfaces, global zones generators integrated into Instrument (no more global zones). * libinstpatch/IpatchSF2IZone.c: Converted to using IpatchSF2GenItem interface, title notify now depends on linked item title. * libinstpatch/IpatchUnit_generic.c: Wasn't setting unit ID field of built in types (fixed), some other changes to the labels and descriptions of built in unit types, all float types changed to double (for her pleasure). * libinstpatch/IpatchSF2Preset.c: Global preset zones now integrated into presets (no more global zones), using new generator item interface. * libinstpatch/IpatchSF2Gen.c: Added generator item interface for items implementing generator properties (IpatchSF2Inst, IpatchSF2IZone, IpatchSF2Preset, IpatchSF2PZone). Some rename of generator property names to more appropriate ones ("key-range" -> "note-range", "root-note-override" -> "root-note", etc?), other cleanup/reorg. * libinstpatch/IpatchUnit_SF2.c: Some renaming of functions, changes to unit labels and descriptions, re-added tenth percent unit, using doubles instead of floats. * libinstpatch/IpatchGigRegion.c: Renamed property "key-range" to "note-range". * libinstpatch/IpatchUnit_DLS.c: More changes like to other unit files. * libinstpatch/IpatchParamProp.h: Added IPATCH_PARAM_NO_SAVE_CHANGE property flag to indicate if a property should cause ipatch_item_changed() to be called (modifies saveable state). * libinstpatch/IpatchSF2PZone.c: Similar changes as with instrument zones. * libinstpatch/IpatchDLS2Region.c: Rename of key-range to note-range. * libinstpatch/IpatchSF2Gen_tables.c: Update of descriptions and labels and renames. * libinstpatch/IpatchSF2VoiceCache_SF2.c: Updated for new global generator changes. * libinstpatch/IpatchSF2Mod.c: New moderator interface. * libinstpatch/IpatchContainer.c: Add/remove notify system, fixed bug in ipatch_container_get_children() with child type checking. * Lots of other minor/major changes that I don't feel like writing about.. Too long without a commit! Sheeesh! 2006-08-31 Josh Green * libinstpatch/IpatchSF2Reader.c: Improper use of ipatch_riff_read_chunk_verify() introduced a bug with recent changes to that function (causing non 24 bit SoundFont files to fail to load). Now enforcing that a SMPL chunk exists, pretty useless without one. 2006-08-29 Josh Green * libinstpatch/IpatchCramDecoder.c: Fixed bug which caused decoder to fail when attempt was made to decode stereo non-split audio. * utils/cram.c: Fixed bug with -p switch (was actually set as --qlp-coeff-search instead of --path), -q, --quiet now doing what it is supposed to do. 2006-08-29 Josh Green A stripped down version of FLAC is now included with libInstPatch to allow for customizations and to prevent breakage from new changes in official FLAC encoder/decoder. * libinstpatch/sample.c (ipatch_sample_format_bit_width): New function for getting the effective bit width of a sample format. * libinstpatch/IpatchRiffParser.c (ipatch_riff_read_chunk_verify): A new error IPATCH_RIFF_ERROR_UNEXPECTED_CHUNK_END was added which is returned if current LIST chunk ends. Previously no error info was being set. * libinstpatch/IpatchSF2File.c: Addition of sample24-pos property and related fields for 24 bit SoundFont support. * libinstpatch/IpatchCramEncoder.c: Changed to use included FLAC, added sample split support (for 24 bit SoundFonts), renamed ipatch_cram_encoder_declare_flac_audio to ipatch_cram_encoder_declare_audio and now passing parameters as a structure which can be allocated with ipatch_cram_encoder_audio_params_new, remove unneeded convert_audio_format_interleave, CRAM read version is getting set correctly if new features are used (such as sample splits), removed metadata hacks (no longer required with included FLAC). * libinstpatch/IpatchCramDecoder.c: Changed to use included FLAC, added sample split support, removed metadata hacks (no longer required with included FLAC). * libinstpatch/IpatchSampleData.c: The flag IPATCH_SAMPLE_DATA_FIND_QUALITY can now be used to search for highest quality sample within a IpatchSampleData object. * libinstpatch/IpatchDLS2Region.c: Adding effect parameter support (not yet completed). * libinstpatch/IpatchTypeProp.c: Renamed ipatch_type_register_func and ipatch_type_get_func to ipatch_type_register_dynamic_func and ipatch_type_get_dynamic_func respectively. * libinstpatch/IpatchSampleStoreSplit24.[ch]: Added for SoundFont 24 support. * libinstpatch/IpatchSF2Reader.c: Added SoundFont 24 bit support. * libinstpatch/util.c (ipatch_util_file_size): New convenience utility function for getting a file size. * libinstpatch/IpatchCram_SF2.c: Added SoundFont 24 bit support, unfortunately due to expected issues with FLAC it results in rather poor performance :( * libinstpatch/IpatchCramDecoderConverter.c: Added proper error detection for ipatch_cram_decoder_next_file in convert method. * libinstpatch/IpatchDLSReader.c: DLS 8 bit audio is now properly set as unsigned. * libinstpatch/IpatchSF2.c: Added "samples-24bit" property which indicates that sample data should be stored as 24 bit. * libinstpatch/IpatchSF2Writer.c: SoundFont 24 bit support. * libinstpatch/IpatchCram.h: Revision 3 of the specification adds sample split support and clarifies some other things in the format documentation. 2006-07-22 Josh Green * IpatchAudioFile type was not being wraped as a python object causing crash with create_sf2.py. * Updated create_sf2.py to use new converter functions. 2006-06-18 Josh Green * libinstpatch/IpatchDLS2Sample.c: Changed init types of enum and flags GValues for IpatchItem property notifies to specific types. * libinstpatch/IpatchGigRegion.c: IpatchRange was incorrectly being freed using g_free. * libinstpatch/IpatchSF2VoiceCache_SF2.c: Fixed bugs in preset and instrument converters, including a GSlice related crash bug and modulator leaks. Also now more resiliant to duplicate global zones. * libinstpatch/IpatchPaste.c: Added ipatch_paste_object_add_duplicate() function. Default paste handler now supports pasting to virtual containers. 2006-05-08 Josh Green * Renamed all functions with *_ref_* in the name to *_get_* and removed _ref from the end of any function names. This was decided to simplify the auto Python binding generation which does not care about reference counting. * IpatchSF2VoiceCache converters now expect a voice cache object to be supplied, rather than creating it in the conversion function. This allows for parameters to be set on the voice cache prior to conversion. * libinstpatch/IpatchVirtualContainer (IPATCH_VIRTUAL_CONTAINER_CREATE): Added childtype field to specify the child types contained by a virtual container. (IpatchVirtualContainerConformFunc): Function type used for "virtual-child-conform-func" type property for making a child conform to a virtual container parent's criteria. * libinstpatch/IpatchSampleData.c (ipatch_sample_data_get_blank): Now setting sample data to zero for blank sample (bug). * libinstpatch/IpatchTypeProp.c: Added "virtual-child-type" property for specifying what type of children a virtual container stores and "virtual-child-conform-func" as explained above. * libinstpatch/IpatchSF2Mod.c: IpatchSF2ModList is now a boxed type for use as a object property. Added functions ipatch_sf2_mod_list_get_type, ipatch_sf2_mod_list_free, ipatch_sf2_mod_list_insert, ipatch_sf2_mod_list_remove, and ipatch_sf2_mod_list_change. Added modulator property support to objects for which it makes sense. * libinstpatch/misc.c: Added "virtual-child-conform-func" functions for percussion/melodic virtual container branches. * libinstpatch/IpatchSF2.c: find_unused_locale method now properly handles searching for free percussion MIDI locale. * libinstpatch/IpatchDLS2.c: find_unused_locale method now properly handles searching for free percussion MIDI locale, fixed bug in ipatch_dls2_make_unique_name which caused unique counter to be overwritten. * Documentation updates for renames. 2006-04-15 Josh Green * Moved ac_python_devel.m4 to m4/python.m4. * builtin_enums.[ch] and marshals.[ch] now auto generated. * gtk-doc documentation updates. * libinstpatch/IpatchSF2VoiceCache_SF2.c: Fixed crash bug in IpatchSF2Sample -> IpatchSF2VoiceCache converter, added voice cache item declaration in IpatchSF2Inst -> IpatchSF2VoiceCache converter. 2006-04-12 Josh Green * libinstpatch/IpatchParamProp.h: Added IPATCH_PARAM_SYNTH and IPATCH_PARAM_SYNTH_REALTIME GParamSpec flags. * libinstpatch/IpatchRange.[ch] (ipatch_range_new): Added low and high parameters. * libinstpatch/IpatchSF2Gen.h: Some renaming of SoundFont generator defines (and property names), generator table is now defined const. * IpatchSF2VoiceCache.c: Added item_func which gets called with all items for which a voice cache depends on (for synthesis updates). (ipatch_sf2_voice_cache_select): Fixed bug that caused not all voice indexes to be used (was counting all voices instead of only matched voices). * IpatchSF2Zone.c: Now setting IPATCH_PARAM_SYNTH and IPATCH_PARAM_SYNTH_REALTIME for zone parameters. * IpatchSF2Zone.h: Fixed generator array macros. * python/ipatch.override: Created an iterator type for IpatchList, IpatchRange can be treated like a sequence and added low and high methods. * python/ipatch-types.defs: New file added to override and declare some types (boxed types, etc). * python/ipatch.defs: No longer auto generated. 2006-03-15 Josh Green * Updated gtk-doc build files and API docs are now disabled by default. * Removed all GMemChunk usage and replaced with GSlice (fallback to malloc if GSlice not available). * Removed PREALLOC defines for objects since it was kinda messy and not really necessary. * libinstpatch/IpatchSF2.c (ipatch_sf2_item_copy): Updated to ignore link_func since no objects are external from the SoundFont itself, fixed bugs where object references weren't being replaced. * libinstpatch/IpatchSF2VoiceCache.[ch]: Added a default_loop_type field which gets used for object types which have no looping mode defined (such as IpatchSF2Sample objects). * libinstpatch/IpatchSampleStoreVirtual.c: Now allows for stereo pairing of mono samples, still need to add stereo->mono conversions. 2006-03-09 Josh Green * libinstpatch/sample.c: Floating point is now -1.0 to 1.0. * libinstpatch/IpatchItem.c (ipatch_item_prop_notify_by_name): Debug macros weren't allowing old_value or new_value to be NULL. 2006-03-03 Josh Green * libinstpatch/IpatchConverter.c: Added IPATCH_CONVERTER_FLAG_SRC_DERIVED flag to ipatch_register_converter_map and changed priority field to flags field. Added ipatch_converter_set_link_funcs(). * libinstpatch/IpatchPaste.c: Added ipatch_paste_object_add_convert() for easily converting an object during a paste operation. * libinstpatch/IpatchVirtualContainer.h: IPATCH_VIRTUAL_CONTAINER_CREATE macro to assist in creating new virtual container types. 2006-01-20 Josh Green IpatchItem copy method changed to use a callback function for fixing up object links and return code removed (copies should not fail), all IpatchItem derived types updated to use this new method. * libinstpatch/IpatchDLS2Region.h: Started adding region properties. * libinstpatch/IpatchItem.[ch]: IpatchItem "copy" method now uses a callback function to fixup external links and return value and GError removed (should not fail), many new functions added for copying and duplicating items (replacement hash, link func, deep duplication), functions added for getting unique properties for an item and testing for conflicts. * libinstpatch/IpatchPaste.[ch]: Overhauled and sweet! :) There is now a paste instance which paste operations can be added to. All items to be pasted are duplicated, conflicts can be detected all at once, items can be changed in place, the paste operations are not executed until finalized. 2005-11-25 Josh Green IpatchConverter system modified to accept a range of types to match for source and destination, many convenience functions added for converting directly between 2 objects or an object and a type. Existing converters updated to use new system. 2005-11-05 Josh Green IpatchItem property notifies now only occur once, after the property is set. The previous value is passed as an argument to the notify. Insane amount of changes on property notifies, still some work to do. Some duplicate properties removed (like "sample" for instrument zones, "link-item" now used where appropriate). Removed "title" type property and made it an IpatchItem property. Some fixes to sample converters to get things to work right for sample exporting. Added ipatch_convert_objects and ipatch_convert_object_to_type functions for added convenience. Bug fixes in GigaSampler writer/reader and sub regions. IpatchItem now has some other defined properties too. Added a "float-digits" parameter property. So much other crap its scary! :) 2005-09-18 Josh Green Applied Win32 build patch from Keishi Suenaga. 2005-09-17 Josh Green Converted all IpatchItem "describe" methods to "title" type properties. Lots of other changes which I don't feel like describing today :) libinstpatch/IpatchItem.c: Removed "describe" method and functions, now replaced by "title" type property, changed implimentations throughout. libinstpatch/IpatchSample.c: Added some IpatchSample interface helper functions for use by IpatchSampleData based interfaces. New loop_types field of IpatchSampleIface to describe what loop types an interface supports. Updated all IpatchSample interfaces. libinstpatch/IpatchDLSReader.c: Fixed an allocation bug with IpatchDLS2SampleInfo in IpatchGigSubRegion objects which was causing crashes. libinstpatch/IpatchGig.c: Added virtual container child types. libinstpatch/IpatchTypeProp.c: Added a "link-item" property libinstpatch/IpatchSF2.c: Bug fix with virtual child list. 2005-08-26 Josh Green Updates to python binding. Created a IpatchContainer.add () method, fixed a bunch of bugs with mis-named constructor types (made it impossible to instantiate many types). Attempted to create a binding for the IpatchRange boxed type. libinstpatch/IpatchAudioFile.c: Fixed an endian related bug (virtual format must be set, otherwise it defaults to the host endian order), less Nazi loop handling, if a conversion is required (non multiple of 8 bit width) then convert to native endian also. libinstpatch/IpatchDLSReader.c: Fixed another endian related bug that was causing DLS and Gig files to fail on big endian machines. libinstpatch/IpatchSF2IZone.c: Fixed some property related bugs. libinstpatch/IpatchSF2PZone.c: Fixed some more property bugs. libinstpatch/IpatchSF2VoiceCache.c (ipatch_sf2_voice_cache_select): Fixed a crash bug when no voices have been added to the cache. libinstpatch/IpatchSF2Zone.c: Added ipatch_sf2_zone_set_key_range and ipatch_sf2_zone_set_velocity_range. libinstpatch/IpatchSampleData.c: Added ipatch_sample_data_ref_primary_store. libinstpatch/IpatchSampleStore.c (ipatch_sample_store_copy): Now allow source sample store to be smaller than destination. IpatchVirtualContainer_types.c: Added Gig virtual container types back in. libinstpatch/misc.c: Initializing Gig virtual container types. 2005-08-24 Josh Green libinstpatch/IpatchSF2VoiceCache_DLS.h: Forgot to add defines for the DLS2Region voice synthesis converter. 2005-08-23 Josh Green autogen.sh: Now checks to see if binaries exist, runs libtoolize and outputs an error message if any commands fail. configure.ac: PKG_CHECK_MODULES is done for pygtk if pygobject not found (for pygtk versions that don't install a pygobject pkg-config file). libinstpatch/IpatchSF2VoiceCache_DLS.c: Added basic DLS2Inst SF2 voice cache synthesis. 2005-08-22 Josh Green configure.ac: PYGTK now uses pygobject-2.0 instead which removes GTK dependency for Python binding. libinstpatch/IpatchSF2VoiceCache_Gig.c: Added IpatchGigSample voice cache synthesis converter. libinstpatch/IpatchSF2VoiceCache_DLS.[ch]: New files, with a IpatchDLS2Sample voice cache synthesis converter. 2005-08-17 Josh Green Virtual container types added for DLS2, SF2, SF2Sample and Gig. Virtual containers are used for grouping items in a user friendly manner, for example: DLS2 objects have Melodic, Percussion and Sample virtual types which would contain Melodic Instruments, Percussion Instruments and Samples respectively in a user interface. libinstpatch/IpatchContainer.c: Added ipatch_container_get_virtual_types which will retrieve a list of virtual container types (grouping for user interfaces). libinstpatch/IpatchCramDecoderConverter: Added "strip-paths" property which will strip all path components from extracted files. libinstpatch/IpatchCramFile.c: Fixed bug in IpatchCramFile identification method which caused CRAM files to not be identified. libinstpatch/IpatchDLS2Inst.c: Added "percussion" property, and percussion flag no longer stored in Bank. libinstpatch/IpatchDLSReader.c: Heavily modified Gig loading code to account for the fact that IpatchGigRegion is no longer inherited from IpatchDLS2Region. libinstpatch/IpatchDLSWriter.c: Heavily modified Gig writing code to account for the fact that IpatchGigRegion is no longer inherited from IpatchDLS2Region. libinstpatch/IpatchGig.c: Now returns IpatchGigInst and IpatchGigSample as child types (overrides IpatchDLS2). libinstpatch/IpatchGigEffects.c: Added ipatch_gig_effects_init to initialize a Gig effects structure. libinstpatch/IpatchGigRegion.c: No longer inhereted from IpatchDLS2Region, so some code is now duplicated. Also dimensions and sub regions now are now objects. Renamed ipatch_gig_region_add_dimension to ipatch_gig_region_new_dimension. libinstpatch/IpatchItem.c: Added mutex inheritence so that a child item can inherit its parent's mutex, thereby saving space and simplifying locking. Currently used for IpatchGigSubRegion and IpatchGigDimension child objects of IpatchGigRegion. libinstpatch/IpatchIter.c: Added array iterator type, split iterator private access macros into GET and SET variants for all iterator types. libinstpatch/IpatchParamProp.c: Added a "unit-type" parameter property to specifiy what units (IpatchUnits.[ch]) a parameter uses. libinstpatch/IpatchSF2Gen.c: Fixed a 64 bit constant bug with generator add mask that was affecting ipatch_sf2_gen_array_offset and causing incorrect results of generator sumation (velocity/key ranges were being added and other valid generators were not!). libinstpatch/IpatchSF2Preset.c: Added "percussion" property. libinstpatch/IpatchSF2Zone.c: Generator "unit-type" parameter properties are now being set. libinstpatch/IpatchTypeProp.[ch]: Added dynamic type property functions which get called when retrieving a type property and handles returning the value. Added "category", "virtual-parent-type", and "sort-children" type properties. Added some default type categories. Added ipatch_type_object_get, ipatch_type_object_get_valist, ipatch_type_object_get_property, and ipatch_type_register_func. libinstpatch/misc.c: Type properties like "name", "blurb", "category", "virtual-parent-type" and "sort-children" now set for built in types. libinstpatch/libinstpatch.h.in: Added G_BEGIN_DECLS and G_END_DECLS, probably should add to every header file though. 2005-06-24 Josh Green Improved finalization for many object types. SF2Voices replaced with IpatchSF2VoiceCache. libinstpatch/IpatchAudioFile.c (ipatch_audio_file_open): Added support for reading and writing loop and instrument info. libinstpatch/IpatchBase.c: Removed ipatch_base_save which has been superceded by IpatchConverter system, added ipatch_base_find_item_by_midi_locale_ref for finding a child item by MIDI locale. libinstpatch/IpatchContainer.c: Added ipatch_container_remove_all and now using it at container finalization. libinstpatch/IpatchGigEffects.c: Unknown info now loaded and preserved. libinstpatch/IpatchSF2VoiceCache.c: Some bug fixes and improvements. libinstpatch/IpatchSF2VoiceCache_SF2.c: Now working. libinstpatch/IpatchSF2VoiceCache_Gig.c: Also working :) 2005-05-06 Josh Green Moved IPATCH_SF2_DEFAULT_NAME to IPATCH_BASE_DEFAULT_NAME. Default name now set for DLS and GigaSampler files. Added "name" and "blurb" IpatchTypeProp properties for defining user friendly name and descriptions for GTypes and defined for SF2, DLS2 and Gig types. 2005-05-02 Josh Green Added IpatchGigSample, IpatchIfaceProp (interface properties), IpatchParamProp (GParamSpec properties) and IpatchTypeProp (GType properties). Copyright message updated in all files and changed GPL license text to only allow use of version 2 (in case of future license corruptions). GigaSampler saving is working now and many improvements in DLS/Gig loader as well. Added software and comment fields to CRAM header and bumped format version to 2. Added properties to IpatchCramDecoder. IpatchCramDecoderConverter now passes property requests to IpatchCramDecoder. Added a get_size method to IpatchFile and ipatch_file_get_size() function to use it. Added a ipatch_file_buf_memset() function, ipatch_file_buf_zero is now just a macro using this function. Bug fixes in item_copy methods for DLS/Gigasampler. Unknown GigaSampler fields are now preserved. Improved cram utility statistics and added flag to set comment and a file list mode. 2004-12-14 Josh Green Lots of changes to sample related property names and loop types. Lots of other property changes in regards to new IpatchParam extended properties - max string lengths, unique tagging, etc. Many objects now have IpatchSample interfaces. * libinstpatch/IpatchSample.[ch]: New - basic sample interface which is now implemented by IpatchDLS2Region, IpatchDLS2Sample, IpatchSF2IZone, IpatchSF2Sample, IpatchSampleData and IpatchSampleFile. * libinstpatch/IpatchUnit_generic.[ch]: New - generic unit definitions and functions moved out of IpatchUnit.[ch]. * libinstpatch/IpatchItem.[ch]: Added a pre_prop_notify_hook which gets called before an IpatchItem property changes, and the symantics of prop_notify_hook have now been changed so it is called after the property has changed. * libinstpatch/IpatchParam.[ch]: Added functions for adding extended properties to GParamSpec types and created some standard properties "string-max-length" to specify max length of strings and "unique-group-id" for specifying a group of properties that define a unique key - an example is Bank:Preset numbers, IPATCH_PARAM_UNIQUE flag added to indicate unique properties. * libinstpatch/IpatchSampleTransform.c: Max frames that can be converted at a time with an IpatchSampleTransform can now be retrieved and other changes. 2004-11-19 Josh Green * libinstpatch/IpatchDLS2Sample.c (ipatch_dls2_sample_find_store_ref): Added sample format parameter. * libinstpatch/IpatchSF2Sample.c (ipatch_sf2_sample_find_store_ref): Added sample format parameter. * libinstpatch/IpatchSampleData (ipatch_sample_data_find_store_ref): Added format parameter and IPATCH_SAMPLE_DATA_FIND_EXACT_FORMAT flag. * libinstpatch/IpatchSampleStore.c (ipatch_sample_store_duplicate): Added sample format parameter. * libinstpatch/sample.c: Fixed bugs in sample endian swapping functions (swap16/32/64). 2004-11-08 Josh Green Documentation updates. Container child_types method changed. * configure.ac: PyGtk codegen dir now fetched with pkg-config. * python/Makefile.am: PyGtk codegen dir now fetched with pkg-config. * libinstpatch/IpatchContainer.c: Removed n_children fields from ipatch_container_get_child_types, ipatch_container_type_get_child_types and class method. 2004-11-07 Josh Green * ac_python_devel.m4: AC_PYTHON_DEVEL for detecting python support. * configure.ac: Updated to use AC_PYTHON_DEVEL in ac_python_devel.m4. * libinstpatch/IpatchConvert_Gig.c: Added Gig file load converter. * libinstpatch/IpatchDLS2Region.c: Regions now use IpatchRange type for velocity and note range. * libinstpatch/IpatchDLSReader.c: Fixed bug with stereo sample sizes, no more debug spew for unknown chunks. * libinstpatch/IpatchSampleFile.c: "channels" property changed to integer type and updated for new sample format channels value. * libinstpatch/IpatchSampleStore.c: "channels" property changed to integer type and updated for new sample format channels value. * libinstpatch/IpatchSampleTransform.c: Updated for new sample format channels value. * libinstpatch/sample.c: Fixed bug with stereo to mono convert functions, updated for new sample format channels value. * libinstpatch/sample.h: Values for sample format channels changed to the number of channels - 1 and a new field added for channel routing. * python/Makefile.am: Updated to use new defines from AC_PYTHON_DEVEL. 2004-10-28 Josh Green "-ref" was removed from all object properties with this naming convention since GValue already references returned objects. 2004-09-16 Josh Green Release: 1.0.0beta-20040917 * examples/create_sf2.py: Updated for latest API changes and to match functionality of C equivelent create_sf2.c. 2004-09-16 Josh Green * examples/create_sf2.c: Updated SoundFont creation example to also create Instruments and Presets for the added samples. * libinstpatch/IpatchSF2Inst.c (ipatch_sf2_inst_create_zone): Killed the return value for ultra convenience (unref not needed). * libinstpatch/IpatchSF2Preset.c (ipatch_sf2_preset_create_zone): Killed the return value for ultra convenience (unref not needed). 2004-09-15 Josh Green Documentation updates. * libinstpatch/IpatchAudioFile.c: Bug fixes. * libinstpatch/IpatchConvert_SF2.c: Improved IpatchSampleFile to IpatchSF2Sample conversion. * libinstpatch/IpatchConverter.[ch]: Renamed ipatch_lookup_converter to ipatch_find_converter and added find flag fields to parameters. * libinstpatch/IpatchSampleData.h: Renamed IpatchSampleStoreFindFlags to IpatchSampleDataFindFlags. * libinstpatch/IpatchSF2Writer.c: Samples now written with "untitled%d" string if it has no name, also fixes crash bug. * libinstpatch/IpatchSampleTransform.c: Bug fixes and new function ipatch_sample_transform_free_buffers. * examples/create_sf2.py: New Python example for creating a SoundFont from sample files. * examples/create_sf2.c: New C example for creating a SoundFont from sample files. 2004-09-01 Josh Green Documentation updates. * libinstpatch/sample.[ch]: Re-worked sample format integer to describe complete sample format (width/type, sign, endian, channels). Moved sample format related functions to sample.c and created conversion functions for converting between any supported format with a routine to create an array of function pointers required for a conversion. * libinstpatch/IpatchSampleTransform.[ch]: A new sample format conversion object. * libinstpatch/IpatchSampleStoreVirtual.[ch]: The beginnings of a virtual sample store, which will support simple edit lists and format conversions. * libinstpatch/IpatchConverter_priv.h: Some private macros for more easily defining IpatchConverter types. * libinstpatch/IpatchAudioFile.h: Removed virtual file support, since we are handling format conversion internally. Added a method for getting a list of supported save file types. Re-wrote sections of open method to use native audio format since virtual IpatchSampleFile support has been ditched. * libinstpatch/IpatchConvert_DLS2.c: Now using helper macros to simplify creation of converter types. Added a IpatchSampleFile to IpatchDLS2Sample converter. Updated init routine for ipatch_register_converter changes. * libinstpatch/IpatchConvert_SF2.c: Added a IpatchSampleFile to IpatchSF2Sample converter. Updated init routine for ipatch_register_converter changes. Now using converter helper macros in IpatchConverter_priv.h. * libinstpatch/IpatchConverter.c: Added fields for expected source and destination item counts to converter registration system and updated the ipatch_register_converter function and changed/renamed ipatch_lookup_converter_type to ipatch_lookup_converter_info to handle this additional information. Updated verify routine to check items based on the item count and type information if the verify method is NULL. * libinstpatch/IpatchDLSReader.c: Updated for new sample format system. * libinstpatch/IpatchDLSWriter.c: Updated to use new sample conversion system. * libinstpatch/IpatchSF2Reader.c: Updated for new sample format system. * libinstpatch/IpatchSF2Writer.c: Updated to use new sample conversion system. * libinstpatch/IpatchSampleData.[ch]: Updated for new sample format system. * libinstpatch/IpatchSampleFile.[ch]: Removed virtual sample format parameters (we now do this internally). Updated for new sample format system. * libinstpatch/IpatchSampleStore.[ch]: Updated for new sample format system. Added convenience functions for converting audio format to/from sample stores. 2004-07-16 Josh Green * libinstpatch/IpatchConvert_SF2.c: Fixed bug in verify method template macro. * libinstpatch/IpatchConverter.c: Added a function to lookup source and destination conversion types for a given converter type. * python/ipatchmodule.c: Added #define NO_IMPORT_PYGOBJECT since it fixes build on MAC OS X related to duplicate symbols. Not real sure what this define is for though. 2004-07-15 Josh Green Fixed bug in IpatchSF2Reader.c that affected big endian machines (Mac OS X in particular). 2004-07-14 Josh Green Renamed Cram* to IpatchCram* to fix python binding and just to integrate the beast. Renamed IpatchSF2Loader to IpatchSF2Reader and IpatchDLSLoader to IpatchDLSReader. Created converters for loading/saving SoundFont and DLS files. Ditched libsndfile support in favor of requiring libaudiofile and added IpatchSampleFile support for libaudiofile with virtual format conversion for files and IpatchSampleStore objects! Renamed IpatchSampleStoreFormat to IpatchSampleFormat and moved it to misc.h. Documentation updates. * libinstpatch/IpatchConverter.c: Lookups for registered handlers now looks for derived types as well and finds the best match. Also added an init() method for converters that want to intialize parameters based on input/output objects. * libinstpatch/IpatchFile.c: Removed load_object method in favor of IpatchConverter system. * libinstpatch/IpatchSampleFile.c: Cleaned up sample format and virtual sample format parameters. 2004-07-06 Josh Green Mucking around with gettext stuff. Hopefully everything is set up right now (what a pain!). 2004-07-06 Josh Green Build updates to intl and po gettext stuff. * libinstpatch/IpatchConverter.c: Documentation updates. * libinstpatch/IpatchRange.c: Now setting value_type field of GParamSpec to IPATCH_TYPE_RANGE (what a GBoxed type should do). * libinstpatch/misc.h: Bug fix to G_HAVE_GNUC_VARARGS version of ipatch_code_error macro. 2004-07-03 Josh Green Release: 1.0.0beta-20040703 Documentation updates (looking pretty sweet). 2004-07-02 Josh Green Dude, I have been bad about the ChangeLog updates! FlacPak is now Cram and the format has been re-written to be extendable (thanks to EBML) and support multiple files in an archive with paths. The IpatchConverter is now being used for the Cram decoder/encoder to convert between IpatchFile and CramFile types. The cram command line utility is working but needs some work in relation to progress and statistic output. 2004-01-25 Josh Green Renamed ipatch_sf2_write to ipatch_sf2_writer_save and swapped IpatchSF2File and IpatchSF2 parameters in ipatch_sf2_writer_new. Both changes made to get Python binding to work correctly. 2004-01-21 Josh Green Removed include of convert.h, which no longer exists, from libinstpatch.h.in. Changed python/Makefile.am to not be broken in regards to generating defs. 2004-01-18 Josh Green Removed IpatchConvert from build until its working, fixed a bug that made zone generator properties unusable, also changed python binding Makefile to generate the defs file if it doesn't already exist. 2003-12-13 Josh Green Lots of bug fixes to SoundFont writer (was pretty much unusable, gotta test those things :) 2003-12-11 Josh Green Removed old object conversion interface and created new IpatchConverter object and conversion registry. Some bug fixes to SoundFont FlacPak encoder handler. 2003-12-03 Josh Green Many bug fixes with FlacPak including a problem with MD5 function name clashes with FLAC causing memory leaks, fixes to compressing or decompressing multiple files, improved statistic functionality with FlacPakEncoder, better text output from flacpak utility and some bugs with the SoundFont FlacPak handler. 2003-12-01 Josh Green FlacPak is on the scene (libinstpatch/FlacPak*), dynamically generating libinstpatch.h since FlacPak is optional, split IpatchFileBuf stuff into separate C file, many bug fixes to IpatchFile routines, made many IpatchDLSLoader routines public (still needs some work), added a flacpak command line program to utils/. 2003-10-03 Josh Green Lots of things have changed, finally getting around to importing this lot into CVS. 2003-08-29 Josh Green libInstPatch is now its own package, check Swami changelog for earlier history. libinstpatch-1.1.6/README.md000066400000000000000000000042301400263525300154610ustar00rootroot00000000000000# libInstPatch ### Copyright (C) 1999-2021 Element Green and others http://www.swamiproject.org [![Build Status](https://dev.azure.com/tommbrt/tommbrt/_apis/build/status/swami.libinstpatch?branchName=master)](https://dev.azure.com/tommbrt/tommbrt/_build/latest?definitionId=1&branchName=master) ## 1. What is libInstPatch? libInstPatch stands for lib-Instrument-Patch and is a library for processing digital sample based MIDI instrument "patch" files. The types of files libInstPatch supports are used for creating instrument sounds for wavetable synthesis. libInstPatch provides an object framework (based on GObject) to load patch files into, which can then be edited, converted, compressed and saved. More information can be found on the libInstPatch Wiki pages on the Project Swami website. http://www.swamiproject.org ## 2. License See [Copying](https://github.com/swami/libinstpatch/blob/master/COPYING). ## 3. Requirements Look at the INSTALL file for instructions on compiling and installing libInstPatch and for more details on software requirements. libInstPatch has the following requirements: - glib/GObject >= 2.14 - libsndfile glib/gobject homepage: http://www.gtk.org libsndfile homepage: http://www.mega-nerd.com/libsndfile libInstPatch can be built for Linux, Mac OSX and many other Unix like operating systems. It has also been known to run on Windows systems. ## 4. Features * Native GObject C API * Supports SoundFont 2 * SoundFont synthesis cache subsystem (IpatchSF2VoiceCache) * Conversion of most raw sample width formats (8/16/24/32bit/float/double, etc) * Sample format transform functions support up to 8 channels * Sample cache pool for caching samples in RAM in different formats * Sample edit lists for primitive sample editing operations * Simple XML tree parsing/saving using the glib GNode data type * Paste subsystem for easily performing copies of objects within and between different instrument files * Instrument item conversion sub system * Incomplete support for DLS 1/2 and GigaSampler ## 5. Trademark Acknowledgement SoundFont is a registered trademark of E-mu Systems, Inc. All other trademarks are property of their respective holders. libinstpatch-1.1.6/TODO.tasks000066400000000000000000000101301400263525300160120ustar00rootroot00000000000000 Handle stereo audio correctly with SoundFont files Object to XML save Encoding and decoding of objects to and from XML. Property flag for indicating properties that should not be stored. DLS2 region properties GigEffects properties Cram progress callback Cram encoding statistics IpatchCramContainer A container object for adding arbitrary files for encoding to a cram file. Direct loading of CRAM files Decode all binary data of a CRAM file into memory and then provide seeking and streaming access to the FLAC encoded samples. Make it appear like a regular IpatchFile. Audio edit lists An IpatchSampleStore type which can be used to create virtual stores from sample data in other stores with simple effects (crop, cut, fade, etc). Command line ipatch utility Create a command line utility for processing instrument files. Create a new central instrument format Create new instrument format which attempts to describe synthesis model. To be used for converting between various other supported formats, and possibly end up as the "native" format of libInstPatch. Akai support Support for loading DLS samples using libaudiofile Support loading of sample data in DLS files with libaudiofile (for unsupported formats). libinstpatch-1.1.6/azure-pipelines.yml000066400000000000000000000172221400263525300200460ustar00rootroot00000000000000# C/C++ with GCC # Build your C/C++ project with GCC using make. # Add steps that publish test results, save build artifacts, deploy, and more: # https://docs.microsoft.com/azure/devops/pipelines/apps/c-cpp/gcc jobs: - job: Linux pool: vmImage: 'ubuntu-latest' steps: - script: | sudo apt-get update sudo apt-get -y install \ libglib2.0-0 \ libsndfile-dev displayName: 'Prerequisites' - script: | mkdir build && cd build cmake -DCMAKE_INSTALL_PREFIX=$HOME/libinstpatch_install -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=0 .. make displayName: 'Compile libinstpatch' - job: macOS pool: vmImage: 'macOS-10.14' steps: - script: | brew update || brew update brew upgrade brew install glib gobject-introspection libsndfile pkg-config displayName: 'Prerequisites' - script: | mkdir build && cd build export PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig" cmake -DINTROSPECTION_ENABLED=0 -DCMAKE_INSTALL_PREFIX=$HOME/libinstpatch_install -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=0 .. make displayName: 'Compile libinstpatch' - job: WindowsXP_VS2017_x86 pool: vmImage: 'vs2017-win2016' variables: gtk-bundle: 'http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.24/gtk+-bundle_2.24.10-20120208_win32.zip' libsndfile-url: 'http://www.mega-nerd.com/libsndfile/files/libsndfile-1.0.28-w32.zip' steps: - script: | @ECHO ON mkdir d:\deps || exit -1 cd d:\deps || exit -1 curl -LfsS -o gtk-bundle-dev.zip $(gtk-bundle) || exit -1 curl -LfsS -o libsndfile-dev.zip $(libsndfile-url) || exit -1 7z x -aos -- gtk-bundle-dev.zip > NUL || exit -1 7z x -aos -- libsndfile-dev.zip > NUL || exit -1 REM need to fix the naming of libsndfile otherwise the linker won't find it mv lib\libsndfile-1.lib lib\sndfile.lib || exit -1 mv lib\libsndfile-1.def lib\sndfile.def || exit -1 displayName: 'Prerequisites' - script: | @ECHO ON SET "PATH=d:\deps\bin;%PATH%" DEL /F C:\Strawberry\perl\bin\pkg-config.bat mkdir build && cd build || exit -1 cmake -G "Visual Studio 15 2017" -T "v141_xp" -DLIB_SUFFIX="" -DCMAKE_INSTALL_PREFIX=$(Build.ArtifactStagingDirectory) -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_VERBOSE_MAKEFILE=0 .. || exit -1 cmake --build . --config Debug --target install || exit -1 del $(Build.ArtifactStagingDirectory)\bin\concrt*.dll del $(Build.ArtifactStagingDirectory)\bin\vcruntime*.dll del $(Build.ArtifactStagingDirectory)\bin\msvcp*.dll displayName: 'Compile libinstpatch' - bash: | set -ex art_dir=$(echo "$(Build.ArtifactStagingDirectory)" | sed 's/\\/\//g' | sed 's|\([a-zA-Z]\):|/\1|') cp d:/deps/bin/libglib*.dll d:/deps/bin/libgobject*.dll d:/deps/bin/libgthread*.dll d:/deps/bin/*intl*.dll d:/deps/bin/libsndfile*.dll $art_dir/bin displayName: 'Copy Artifacts' - task: PublishBuildArtifacts@1 inputs: pathtoPublish: $(Build.ArtifactStagingDirectory) artifactName: libinstpatch-Win32 - job: WindowsXP_VS2017_x64 pool: vmImage: 'vs2017-win2016' variables: gtk-bundle: 'http://ftp.gnome.org/pub/gnome/binaries/win64/gtk+/2.22/gtk+-bundle_2.22.1-20101229_win64.zip' libsndfile-url: 'http://www.mega-nerd.com/libsndfile/files/libsndfile-1.0.28-w64.zip' steps: - script: | @ECHO ON mkdir d:\deps || exit -1 cd d:\deps || exit -1 curl -LfsS -o gtk-bundle-dev.zip $(gtk-bundle) || exit -1 curl -LfsS -o libsndfile-dev.zip $(libsndfile-url) || exit -1 7z x -aos -- gtk-bundle-dev.zip > NUL || exit -1 7z x -aos -- libsndfile-dev.zip > NUL || exit -1 REM need to fix the naming of libsndfile otherwise the linker won't find it mv lib\libsndfile-1.lib lib\sndfile.lib || exit -1 mv lib\libsndfile-1.def lib\sndfile.def || exit -1 displayName: 'Prerequisites' - script: | @ECHO ON SET "PATH=d:\deps\bin;%PATH%" DEL /F C:\Strawberry\perl\bin\pkg-config.bat mkdir build && cd build || exit -1 cmake -G "Visual Studio 15 2017 Win64" -T "v141_xp" -DLIB_SUFFIX="" -DCMAKE_INSTALL_PREFIX=$(Build.ArtifactStagingDirectory) -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_VERBOSE_MAKEFILE=0 .. || exit -1 cmake --build . --config Debug --target install || exit -1 del $(Build.ArtifactStagingDirectory)\bin\concrt*.dll del $(Build.ArtifactStagingDirectory)\bin\vcruntime*.dll del $(Build.ArtifactStagingDirectory)\bin\msvcp*.dll displayName: 'Compile libinstpatch' - bash: | set -ex art_dir=$(echo "$(Build.ArtifactStagingDirectory)" | sed 's/\\/\//g' | sed 's|\([a-zA-Z]\):|/\1|') cp d:/deps/bin/libglib*.dll d:/deps/bin/libgobject*.dll d:/deps/bin/libgthread*.dll d:/deps/bin/*intl*.dll d:/deps/bin/libsndfile*.dll $art_dir/bin displayName: 'Copy Artifacts' - task: PublishBuildArtifacts@1 inputs: pathtoPublish: $(Build.ArtifactStagingDirectory) artifactName: libinstpatch-x64 - job: Windows_VS2019_x86 pool: vmImage: 'windows-2019' variables: gtk-bundle: 'http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.24/gtk+-bundle_2.24.10-20120208_win32.zip' libsndfile-url: 'http://www.mega-nerd.com/libsndfile/files/libsndfile-1.0.28-w32.zip' steps: - script: | @ECHO ON mkdir d:\deps || exit -1 cd d:\deps || exit -1 curl -LfsS -o gtk-bundle-dev.zip $(gtk-bundle) || exit -1 curl -LfsS -o libsndfile-dev.zip $(libsndfile-url) || exit -1 7z x -aos -- gtk-bundle-dev.zip > NUL || exit -1 7z x -aos -- libsndfile-dev.zip > NUL || exit -1 REM need to fix the naming of libsndfile otherwise the linker won't find it mv lib\libsndfile-1.lib lib\sndfile.lib || exit -1 mv lib\libsndfile-1.def lib\sndfile.def || exit -1 displayName: 'Prerequisites' - script: | @ECHO ON SET "PATH=d:\deps\bin;%PATH%" DEL /F C:\Strawberry\perl\bin\pkg-config.bat mkdir build && cd build || exit -1 cmake -G "Visual Studio 16 2019" -A Win32 -DCMAKE_INSTALL_PREFIX=$HOME/libinstpatch_install -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=0 .. || exit -1 cmake --build . || exit 1 displayName: 'Compile libinstpatch' - job: Windows_VS2019_x64 pool: vmImage: 'windows-2019' variables: gtk-bundle: 'http://ftp.gnome.org/pub/gnome/binaries/win64/gtk+/2.22/gtk+-bundle_2.22.1-20101229_win64.zip' libsndfile-url: 'http://www.mega-nerd.com/libsndfile/files/libsndfile-1.0.28-w64.zip' steps: - script: | @ECHO ON mkdir d:\deps || exit -1 cd d:\deps || exit -1 curl -LfsS -o gtk-bundle-dev.zip $(gtk-bundle) || exit -1 curl -LfsS -o libsndfile-dev.zip $(libsndfile-url) || exit -1 7z x -aos -- gtk-bundle-dev.zip > NUL || exit -1 7z x -aos -- libsndfile-dev.zip > NUL || exit -1 REM need to fix the naming of libsndfile otherwise the linker won't find it mv lib\libsndfile-1.lib lib\sndfile.lib || exit -1 mv lib\libsndfile-1.def lib\sndfile.def || exit -1 displayName: 'Prerequisites' - script: | @ECHO ON SET "PATH=d:\deps\bin;%PATH%" DEL /F C:\Strawberry\perl\bin\pkg-config.bat mkdir build && cd build || exit -1 cmake -G "Visual Studio 16 2019" -A x64 -DCMAKE_INSTALL_PREFIX=$HOME/libinstpatch_install -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=0 .. || exit -1 cmake --build . || exit 1 displayName: 'Compile libinstpatch' libinstpatch-1.1.6/cmake/000077500000000000000000000000001400263525300152635ustar00rootroot00000000000000libinstpatch-1.1.6/cmake/CheckDIRSymbolExists.cmake000066400000000000000000000066671400263525300222460ustar00rootroot00000000000000# - Check if the DIR symbol exists like in AC_HEADER_DIRENT. # CHECK_DIRSYMBOL_EXISTS(FILES VARIABLE) # # FILES - include files to check # VARIABLE - variable to return result # # This module is a small but important variation on CheckSymbolExists.cmake. # The symbol always searched for is DIR, and the test programme follows # the AC_HEADER_DIRENT test programme rather than the CheckSymbolExists.cmake # test programme which always fails since DIR tends to be typedef'd # rather than #define'd. # # The following variables may be set before calling this macro to # modify the way the check is run: # # CMAKE_REQUIRED_FLAGS = string of compile command line flags # CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) # CMAKE_REQUIRED_INCLUDES = list of include directories # CMAKE_REQUIRED_LIBRARIES = list of libraries to link MACRO(CHECK_DIRSYMBOL_EXISTS FILES VARIABLE) IF(NOT DEFINED ${VARIABLE}) SET(CMAKE_CONFIGURABLE_FILE_CONTENT "/* */\n") SET(MACRO_CHECK_DIRSYMBOL_EXISTS_FLAGS ${CMAKE_REQUIRED_FLAGS}) IF(CMAKE_REQUIRED_LIBRARIES) SET(CHECK_DIRSYMBOL_EXISTS_LIBS "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") ELSE(CMAKE_REQUIRED_LIBRARIES) SET(CHECK_DIRSYMBOL_EXISTS_LIBS) ENDIF(CMAKE_REQUIRED_LIBRARIES) IF(CMAKE_REQUIRED_INCLUDES) SET(CMAKE_DIRSYMBOL_EXISTS_INCLUDES "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}") ELSE(CMAKE_REQUIRED_INCLUDES) SET(CMAKE_DIRSYMBOL_EXISTS_INCLUDES) ENDIF(CMAKE_REQUIRED_INCLUDES) FOREACH(FILE ${FILES}) SET(CMAKE_CONFIGURABLE_FILE_CONTENT "${CMAKE_CONFIGURABLE_FILE_CONTENT}#include <${FILE}>\n") ENDFOREACH(FILE) SET(CMAKE_CONFIGURABLE_FILE_CONTENT "${CMAKE_CONFIGURABLE_FILE_CONTENT}\nint main()\n{if ((DIR *) 0) return 0;}\n") CONFIGURE_FILE("${CMAKE_ROOT}/Modules/CMakeConfigurableFile.in" "${CMAKE_BINARY_DIR}/CMakeFiles/CMakeTmp/CheckDIRSymbolExists.c" @ONLY) MESSAGE(STATUS "Looking for DIR in ${FILES}") TRY_COMPILE(${VARIABLE} ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeTmp/CheckDIRSymbolExists.c COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_DIRSYMBOL_EXISTS_FLAGS} "${CHECK_DIRSYMBOL_EXISTS_LIBS}" "${CMAKE_DIRSYMBOL_EXISTS_INCLUDES}" OUTPUT_VARIABLE OUTPUT) IF(${VARIABLE}) MESSAGE(STATUS "Looking for DIR in ${FILES} - found") SET(${VARIABLE} 1 CACHE INTERNAL "Have symbol DIR") FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeOutput.log "Determining if the DIR symbol is defined as in AC_HEADER_DIRENT " "passed with the following output:\n" "${OUTPUT}\nFile ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeTmp/CheckDIRSymbolExists.c:\n" "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n") ELSE(${VARIABLE}) MESSAGE(STATUS "Looking for DIR in ${FILES} - not found.") SET(${VARIABLE} "" CACHE INTERNAL "Have symbol DIR") FILE(APPEND ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeError.log "Determining if the DIR symbol is defined as in AC_HEADER_DIRENT " "failed with the following output:\n" "${OUTPUT}\nFile ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeTmp/CheckDIRSymbolExists.c:\n" "${CMAKE_CONFIGURABLE_FILE_CONTENT}\n") ENDIF(${VARIABLE}) ENDIF(NOT DEFINED ${VARIABLE}) ENDMACRO(CHECK_DIRSYMBOL_EXISTS) libinstpatch-1.1.6/cmake/CheckPrototypeExists.cmake000066400000000000000000000023741400263525300224360ustar00rootroot00000000000000# - Check if the prototype for a function exists. # CHECK_PROTOTYPE_EXISTS (FUNCTION HEADER VARIABLE) # # FUNCTION - the name of the function you are looking for # HEADER - the header(s) where the prototype should be declared # VARIABLE - variable to store the result # # The following variables may be set before calling this macro to # modify the way the check is run: # # CMAKE_REQUIRED_FLAGS = string of compile command line flags # CMAKE_REQUIRED_DEFINITIONS = list of macros to define (-DFOO=bar) # CMAKE_REQUIRED_INCLUDES = list of include directories # Copyright (c) 2006, Alexander Neundorf, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. INCLUDE(CheckCSourceCompiles) MACRO (CHECK_PROTOTYPE_EXISTS _SYMBOL _HEADER _RESULT) SET(_INCLUDE_FILES) FOREACH (it ${_HEADER}) SET(_INCLUDE_FILES "${_INCLUDE_FILES}#include <${it}>\n") ENDFOREACH (it) SET(_CHECK_PROTO_EXISTS_SOURCE_CODE " ${_INCLUDE_FILES} int main() { #ifndef ${_SYMBOL} int i = sizeof(&${_SYMBOL}); #endif return 0; } ") CHECK_C_SOURCE_COMPILES("${_CHECK_PROTO_EXISTS_SOURCE_CODE}" ${_RESULT}) ENDMACRO (CHECK_PROTOTYPE_EXISTS _SYMBOL _HEADER _RESULT) libinstpatch-1.1.6/cmake/CheckSTDC.cmake000066400000000000000000000023151400263525300177610ustar00rootroot00000000000000message(STATUS "Checking whether system has ANSI C header files") include(CheckPrototypeExists) include(CheckIncludeFiles) check_include_files("dlfcn.h;stdint.h;stddef.h;inttypes.h;stdlib.h;strings.h;string.h;float.h" StandardHeadersExist) if(StandardHeadersExist) check_prototype_exists(memchr string.h memchrExists) if(memchrExists) check_prototype_exists(free stdlib.h freeExists) if(freeExists) message(STATUS "ANSI C header files - found") set(STDC_HEADERS 1 CACHE INTERNAL "System has ANSI C header files") set(HAVE_STRINGS_H 1) set(HAVE_STRING_H 1) set(HAVE_FLOAT_H 1) set(HAVE_STDLIB_H 1) set(HAVE_STDDEF_H 1) set(HAVE_STDINT_H 1) set(HAVE_INTTYPES_H 1) set(HAVE_DLFCN_H 1) endif(freeExists) endif(memchrExists) endif(StandardHeadersExist) if(NOT STDC_HEADERS) message(STATUS "ANSI C header files - not found") set(STDC_HEADERS 0 CACHE INTERNAL "System has ANSI C header files") endif(NOT STDC_HEADERS) check_include_files(unistd.h HAVE_UNISTD_H) include(CheckDIRSymbolExists) check_dirsymbol_exists("sys/stat.h;sys/types.h;dirent.h" HAVE_DIRENT_H) if (HAVE_DIRENT_H) set(HAVE_SYS_STAT_H 1) set(HAVE_SYS_TYPES_H 1) endif (HAVE_DIRENT_H) libinstpatch-1.1.6/cmake/DefaultDirs.cmake000066400000000000000000000032321400263525300204730ustar00rootroot00000000000000# Several directory names used by libInstPatch to install files # the variable names are similar to the KDE4 build system # BUNDLE_INSTALL_DIR - Mac only: the directory for application bundles set (BUNDLE_INSTALL_DIR "/Applications" CACHE STRING "The install dir for application bundles") mark_as_advanced (BUNDLE_INSTALL_DIR) # FRAMEWORK_INSTALL_DIR - Mac only: the directory for framework bundles set (FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "The install dir for framework bundles") mark_as_advanced (FRAMEWORK_INSTALL_DIR) # BIN_INSTALL_DIR - the directory where executables will be installed set (BIN_INSTALL_DIR "bin" CACHE STRING "The install dir for executables") mark_as_advanced (BIN_INSTALL_DIR) # SBIN_INSTALL_DIR - the directory where system executables will be installed set (SBIN_INSTALL_DIR "sbin" CACHE STRING "The install dir for system executables") mark_as_advanced (SBIN_INSTALL_DIR) # LIB_INSTALL_DIR - the directory where libraries will be installed set (LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE STRING "The install dir for libraries") mark_as_advanced (LIB_INSTALL_DIR) # INCLUDE_INSTALL_DIR - the install dir for header files set (INCLUDE_INSTALL_DIR "include" CACHE STRING "The install dir for headers") mark_as_advanced (INCLUDE_INSTALL_DIR) # DATA_INSTALL_DIR - the base install directory for data files set (DATA_INSTALL_DIR "share" CACHE STRING "The base install dir for data files") mark_as_advanced (DATA_INSTALL_DIR) # DOC_INSTALL_DIR - the install dir for documentation set (DOC_INSTALL_DIR "share/doc" CACHE STRING "The install dir for documentation") mark_as_advanced (DOC_INSTALL_DIR) libinstpatch-1.1.6/cmake/FindGObjectIntrospection.cmake000066400000000000000000000040551400263525300231700ustar00rootroot00000000000000# - try to find gobject-introspection # # Once done this will define # # INTROSPECTION_FOUND - system has gobject-introspection # INTROSPECTION_SCANNER - the gobject-introspection scanner, g-ir-scanner # INTROSPECTION_COMPILER - the gobject-introspection compiler, g-ir-compiler # INTROSPECTION_GENERATE - the gobject-introspection generate, g-ir-generate # INTROSPECTION_GIRDIR # INTROSPECTION_TYPELIBDIR # INTROSPECTION_CFLAGS # INTROSPECTION_LIBS # # Copyright (C) 2010, Pino Toscano, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. macro(_GIR_GET_PKGCONFIG_VAR _outvar _varname) execute_process( COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=${_varname} gobject-introspection-1.0 OUTPUT_VARIABLE _result RESULT_VARIABLE _null ) if (_null) else() string(REGEX REPLACE "[\r\n]" " " _result "${_result}") string(REGEX REPLACE " +$" "" _result "${_result}") separate_arguments(_result) set(${_outvar} ${_result} CACHE INTERNAL "") endif() endmacro(_GIR_GET_PKGCONFIG_VAR) find_package(PkgConfig) if(PKG_CONFIG_FOUND) if(PACKAGE_FIND_VERSION_COUNT GREATER 0) set(_gir_version_cmp ">=${PACKAGE_FIND_VERSION}") endif() pkg_check_modules(_pc_gir gobject-introspection-1.0${_gir_version_cmp}) if(_pc_gir_FOUND) set(INTROSPECTION_FOUND TRUE) _gir_get_pkgconfig_var(INTROSPECTION_SCANNER "g_ir_scanner") _gir_get_pkgconfig_var(INTROSPECTION_COMPILER "g_ir_compiler") _gir_get_pkgconfig_var(INTROSPECTION_GENERATE "g_ir_generate") _gir_get_pkgconfig_var(INTROSPECTION_GIRDIR "girdir") _gir_get_pkgconfig_var(INTROSPECTION_TYPELIBDIR "typelibdir") set(INTROSPECTION_CFLAGS "${_pc_gir_CFLAGS}") set(INTROSPECTION_LIBS "${_pc_gir_LIBS}") set(INTROSPECTION_LIBRARIES "${_pc_gir_LIBRARIES}") endif() endif() mark_as_advanced( INTROSPECTION_SCANNER INTROSPECTION_COMPILER INTROSPECTION_GENERATE INTROSPECTION_GIRDIR INTROSPECTION_TYPELIBDIR INTROSPECTION_CFLAGS INTROSPECTION_LIBS ) libinstpatch-1.1.6/cmake/GObjectIntrospectionMacros.cmake000066400000000000000000000070741400263525300235400ustar00rootroot00000000000000# Copyright (C) 2010, Pino Toscano, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. macro(_gir_list_prefix _outvar _listvar _prefix) set(${_outvar}) foreach(_item IN LISTS ${_listvar}) list(APPEND ${_outvar} ${_prefix}${_item}) endforeach() endmacro(_gir_list_prefix) macro(gir_add_introspections introspections_girs) set(_gir_girs) set(_gir_typelibs) foreach(gir IN LISTS ${introspections_girs}) set(_gir_name "${gir}") ## Transform the gir filename to something which can reference through a variable ## without automake/make complaining, eg Gtk-2.0.gir -> Gtk_2_0_gir string(REPLACE "-" "_" _gir_name "${_gir_name}") string(REPLACE "." "_" _gir_name "${_gir_name}") # Namespace and Version is either fetched from the gir filename # or the _NAMESPACE/_VERSION variable combo set(_gir_namespace "${${_gir_name}_NAMESPACE}") if (_gir_namespace STREQUAL "") string(REGEX REPLACE "([^-]+)-.*" "\\1" _gir_namespace "${gir}") endif () set(_gir_version "${${_gir_name}_VERSION}") if (_gir_version STREQUAL "") string(REGEX REPLACE ".*-([^-]+).gir" "\\1" _gir_version "${gir}") endif () # _PROGRAM is an optional variable which needs it's own --program argument set(_gir_program "${${_gir_name}_PROGRAM}") if (NOT _gir_program STREQUAL "") set(_gir_program "--program=${_gir_program}") endif () # Variables which provides a list of things _gir_list_prefix(_gir_libraries ${_gir_name}_LIBS "--library=") _gir_list_prefix(_gir_packages ${_gir_name}_PACKAGES "--pkg=") _gir_list_prefix(_gir_includes ${_gir_name}_INCLUDES "--include=") _gir_list_prefix(_gir_export_packages ${_gir_name}_EXPORT_PACKAGES "--pkg-export=") # Reuse the LIBTOOL variable from by automake if it's set set(_gir_libtool "--no-libtool") add_custom_command( COMMAND ${INTROSPECTION_SCANNER} ${INTROSPECTION_SCANNER_ARGS} --warn-all --quiet --namespace=${_gir_namespace} --nsversion=${_gir_version} ${_gir_libtool} ${_gir_program} ${_gir_libraries} ${_gir_packages} ${_gir_includes} ${_gir_export_packages} ${${_gir_name}_SCANNERFLAGS} ${${_gir_name}_CFLAGS} ${${_gir_name}_FILES} --output ${CMAKE_CURRENT_BINARY_DIR}/${gir} DEPENDS ${${_gir_name}_FILES} ${${_gir_name}_LIBS} OUTPUT ${gir} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} VERBATIM ) list(APPEND _gir_girs ${CMAKE_CURRENT_BINARY_DIR}/${gir}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${gir} DESTINATION share/gir-1.0) string(REPLACE ".gir" ".typelib" _typelib "${gir}") add_custom_command( COMMAND ${INTROSPECTION_COMPILER} ${INTROSPECTION_COMPILER_ARGS} --includedir=. ${CMAKE_CURRENT_BINARY_DIR}/${gir} -o ${CMAKE_CURRENT_BINARY_DIR}/${_typelib} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${gir} OUTPUT ${_typelib} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) list(APPEND _gir_typelibs ${CMAKE_CURRENT_BINARY_DIR}/${_typelib}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${_typelib} DESTINATION lib${LIB_SUFFIX}/girepository-1.0) endforeach() add_custom_target(gir-girs ALL DEPENDS ${_gir_girs}) add_custom_target(gir-typelibs ALL DEPENDS ${_gir_typelibs}) endmacro(gir_add_introspections) libinstpatch-1.1.6/cmake/MacroOptionalFindPackage.cmake000066400000000000000000000036471400263525300231230ustar00rootroot00000000000000# - MACRO_OPTIONAL_FIND_PACKAGE() combines FIND_PACKAGE() with an OPTION() # MACRO_OPTIONAL_FIND_PACKAGE( [QUIT] ) # This macro is a combination of OPTION() and FIND_PACKAGE(), it # works like FIND_PACKAGE(), but additionally it automatically creates # an option name WITH_, which can be disabled via the cmake GUI. # or via -DWITH_=OFF # The standard _FOUND variables can be used in the same way # as when using the normal FIND_PACKAGE() # Copyright (c) 2006-2010 Alexander Neundorf, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # This is just a helper macro to set a bunch of variables empty. # We don't know whether the package uses UPPERCASENAME or CamelCaseName, so we try both: macro(_MOFP_SET_EMPTY_IF_DEFINED _name _var) if(DEFINED ${_name}_${_var}) set(${_name}_${_var} "") endif(DEFINED ${_name}_${_var}) string(TOUPPER ${_name} _nameUpper) if(DEFINED ${_nameUpper}_${_var}) set(${_nameUpper}_${_var} "") endif(DEFINED ${_nameUpper}_${_var}) endmacro(_MOFP_SET_EMPTY_IF_DEFINED _package _var) macro (MACRO_OPTIONAL_FIND_PACKAGE _name ) option(WITH_${_name} "Search for ${_name} package" ON) if (WITH_${_name}) find_package(${_name} ${ARGN}) else (WITH_${_name}) string(TOUPPER ${_name} _nameUpper) set(${_name}_FOUND FALSE) set(${_nameUpper}_FOUND FALSE) _mofp_set_empty_if_defined(${_name} INCLUDE_DIRS) _mofp_set_empty_if_defined(${_name} INCLUDE_DIR) _mofp_set_empty_if_defined(${_name} INCLUDES) _mofp_set_empty_if_defined(${_name} LIBRARY) _mofp_set_empty_if_defined(${_name} LIBRARIES) _mofp_set_empty_if_defined(${_name} LIBS) _mofp_set_empty_if_defined(${_name} FLAGS) _mofp_set_empty_if_defined(${_name} DEFINITIONS) endif (WITH_${_name}) endmacro (MACRO_OPTIONAL_FIND_PACKAGE) libinstpatch-1.1.6/cmake/UnsetPkgConfig.cmake000066400000000000000000000010411400263525300211470ustar00rootroot00000000000000macro ( unset_pkg_config _prefix ) unset ( ${_prefix}_VERSION CACHE ) unset ( ${_prefix}_PREFIX CACHE ) unset ( ${_prefix}_CFLAGS CACHE ) unset ( ${_prefix}_CFLAGS_OTHER CACHE ) unset ( ${_prefix}_LDFLAGS CACHE ) unset ( ${_prefix}_LDFLAGS_OTHER CACHE ) unset ( ${_prefix}_LIBRARIES CACHE ) unset ( ${_prefix}_INCLUDEDIR CACHE ) unset ( ${_prefix}_INCLUDE_DIRS CACHE ) unset ( ${_prefix}_LIBDIR CACHE ) unset ( ${_prefix}_LIBRARY_DIRS CACHE ) unset ( __pkg_config_checked_${_prefix} CACHE ) endmacro ( unset_pkg_config ) libinstpatch-1.1.6/cmake/cmake_uninstall.cmake.in000066400000000000000000000015551400263525300220510ustar00rootroot00000000000000IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) STRING(REGEX REPLACE "\n" ";" files "${files}") FOREACH(file ${files}) MESSAGE(STATUS "Uninstalling \"${file}\"") IF(EXISTS "${file}") EXEC_PROGRAM( "@CMAKE_COMMAND@" ARGS "-E remove \"${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) IF("${rm_retval}" STREQUAL 0) ELSE("${rm_retval}" STREQUAL 0) MESSAGE(FATAL_ERROR "Problem when removing \"${file}\"") ENDIF("${rm_retval}" STREQUAL 0) ELSE(EXISTS "${file}") MESSAGE(STATUS "File \"${file}\" does not exist.") ENDIF(EXISTS "${file}") ENDFOREACH(file) libinstpatch-1.1.6/config.h.cmake000066400000000000000000000045571400263525300167130ustar00rootroot00000000000000#ifndef CONFIG_H #define CONFIG_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ERRNO_H @HAVE_ERRNO_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_FCNTL_H @HAVE_FCNTL_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_IO_H @HAVE_IO_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_MATH_H @HAVE_MATH_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LOCALE_H @HAVE_LOCALE_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDARG_H @HAVE_STDARG_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDIO_H @HAVE_STDIO_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STDLIB_H @HAVE_STDLIB_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_STRING_H @HAVE_STRING_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_STAT_H @HAVE_SYS_STAT_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UNISTD_H @HAVE_UNISTD_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_WINDOWS_H @HAVE_WINDOWS_H@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_XLOCALE_H @HAVE_XLOCALE_H@ /* Define if using the MinGW32 environment */ #cmakedefine MINGW32 @MINGW32@ /* Define to 1 if your C compiler doesn't accept -c and -o together. */ #cmakedefine NO_MINUS_C_MINUS_O @NO_MINUS_C_MINUS_O@ /* Name of package */ #cmakedefine PACKAGE "@PACKAGE@" /* Define to the address where bug reports for this package should be sent. */ #cmakedefine PACKAGE_BUGREPORT @PACKAGE_BUGREPORT@ /* Define to the full name of this package. */ #cmakedefine PACKAGE_NAME @PACKAGE_NAME@ /* Define to the full name and version of this package. */ #cmakedefine PACKAGE_STRING @PACKAGE_STRING@ /* Define to the one symbol short name of this package. */ #cmakedefine PACKAGE_TARNAME @PACKAGE_TARNAME@ /* Define to the version of this package. */ #cmakedefine PACKAGE_VERSION @PACKAGE_VERSION@ /* Define to 1 if you have the ANSI C header files. */ #cmakedefine STDC_HEADERS @STDC_HEADERS@ /* Version number of package */ #cmakedefine VERSION @IPATCH_VERSION@ /* Enable this to include extra debug functions */ #cmakedefine IPATCH_DEBUG @IPATCH_DEBUG@ #endif /* CONFIG_H */ libinstpatch-1.1.6/docs/000077500000000000000000000000001400263525300151335ustar00rootroot00000000000000libinstpatch-1.1.6/docs/CMakeLists.txt000066400000000000000000000002631400263525300176740ustar00rootroot00000000000000# # libInstPatch # # Copyright (C) 1999-2014 Element Green # # See COPYING license file for distribution details # add_subdirectory ( reference ) libinstpatch-1.1.6/docs/reference/000077500000000000000000000000001400263525300170715ustar00rootroot00000000000000libinstpatch-1.1.6/docs/reference/CMakeLists.txt000066400000000000000000000022101400263525300216240ustar00rootroot00000000000000# # libInstPatch # # Copyright (C) 1999-2014 Element Green # # See COPYING license file for distribution details # set (ignore_headers IpatchConvert_Gig.h IpatchConvert_SF2.h IpatchConvert_DLS2.h IpatchConverter_priv.h IpatchSF2VoiceCache_DLS.h IpatchSF2VoiceCache_Gig.h IpatchSF2VoiceCache_SF2.h IpatchSF2VoiceCache_VBank.h IpatchState.h IpatchStateGroup.h IpatchStateItem.h IpatchState_types.h builtin_enums.h compat.h config.h i18n.h ipatch_priv.h libinstpatch.h marshals.h ) if (GTKDOC_FOUND) gtk_doc_add_module (libinstpatch SOURCE ${CMAKE_SOURCE_DIR}/libinstpatch XML libinstpatch-docs.xml SUFFIXES c h IGNOREHEADERS ${ignore_headers} SCANOPTS ${CMAKE_CURRENT_BINARY_DIR}/../../libinstpatch/version.h SCANOBJOPTS --type-init-func=ipatch_init\(\) CFLAGS -I${CMAKE_SOURCE_DIR} LDFLAGS -L${CMAKE_CURRENT_BINARY_DIR}/../../libinstpatch -linstpatch-1.0 LDPATH ${CMAKE_CURRENT_BINARY_DIR}/../../libinstpatch DEPENDS instpatch-1.0 ) add_custom_target(documentation ALL DEPENDS doc-libinstpatch) endif () libinstpatch-1.1.6/docs/reference/libinstpatch-docs.xml000066400000000000000000000124661400263525300232360ustar00rootroot00000000000000 ]> libinstpatch Reference Manual For libinstpatch 1.1.0. The latest version of this documentation can be found on-line at http://www.swamiproject.org/api/libinstpatch/. Base objects and functions Utility objects and functions Sample data objects and functions SoundFont patches DLS patches Spectralis SLI/SLC patches GigaSampler patches Virtual banks Object Hierarchy API Index libinstpatch-1.1.6/examples/000077500000000000000000000000001400263525300160215ustar00rootroot00000000000000libinstpatch-1.1.6/examples/create_sf2.c000066400000000000000000000075341400263525300202130ustar00rootroot00000000000000/* * Example of creating a SoundFont from multiple audio samples * * Updated on Oct 14, 2010 * Some API changes and additional comments in code. * * Updated on Nov 12, 2004 * Now uses more convenient converter functions. * * Joshua "Element" Green - Sep 14, 2004 * Use this example as you please (public domain) */ #include #include int main(int argc, char *argv[]) { IpatchSF2 *sf2; IpatchFileHandle *fhandle; IpatchSF2File *sffile; GObject *sample; /* IpatchSF2Sample */ IpatchSF2Inst *inst; IpatchSF2Preset *preset; GError *err = NULL; char *fname, *name; int i; if(argc < 2) { fprintf(stderr, "Usage: create_sf2 sample1.wav [sample2.wav sample3.aiff ..]\n"); return (1); } /* initialize libInstPatch */ ipatch_init(); sf2 = ipatch_sf2_new(); /* ++ ref new SoundFont object */ /* loop over file names (command line args) */ for(i = 1; i < argc; i++) { fname = argv[i]; /* ++ identify file type and open handle to file object */ fhandle = ipatch_file_identify_open(fname, &err); if(!fhandle) { fprintf(stderr, "Failed to identify file '%s': %s\n", fname, ipatch_gerror_message(err)); g_clear_error(&err); continue; } /* try to convert file to a SoundFont sample */ sample = ipatch_convert_object_to_type(G_OBJECT(fhandle->file), IPATCH_TYPE_SF2_SAMPLE, &err); if(!sample) { fprintf(stderr, "Failed to convert file '%s' to SoundFont sample: %s\n", fname, ipatch_gerror_message(err)); g_clear_error(&err); ipatch_file_close(fhandle); /* -- close file */ continue; } /* append sample to SoundFont (ensure that its name is unique) */ ipatch_container_add_unique(IPATCH_CONTAINER(sf2), IPATCH_ITEM(sample)); g_object_get(sample, "name", &name, NULL); /* get the sample's name */ /* create new SoundFont instrument (++ ref) */ inst = ipatch_sf2_inst_new(); g_object_set(inst, "name", name, NULL); /* set instrument name */ /* create new instrument zone and link sample to it */ ipatch_sf2_inst_new_zone(inst, IPATCH_SF2_SAMPLE(sample)); g_object_unref(sample); /* -- unref SoundFont sample */ /* append instrument to SoundFont (ensure that its name is unique) */ ipatch_container_add_unique(IPATCH_CONTAINER(sf2), IPATCH_ITEM(inst)); /* create new SoundFont preset (++ ref) */ preset = ipatch_sf2_preset_new(); g_object_set(preset, "name", name, NULL); /* set preset name */ /* create new preset zone and link instrument to it */ ipatch_sf2_preset_new_zone(preset, inst); g_object_unref(inst); /* -- unref SoundFont instrument */ /* append preset to SoundFont (ensure name/bank/preset # are unique) */ ipatch_container_add_unique(IPATCH_CONTAINER(sf2), IPATCH_ITEM(preset)); g_object_unref(preset); g_free(name); /* free the name (returned from g_object_get) */ ipatch_file_close(fhandle); /* -- close file */ } /* create SoundFont file object, set its name and open for writing */ sffile = ipatch_sf2_file_new(); ipatch_file_set_name(IPATCH_FILE(sffile), "output.sf2"); /* Save SoundFont to file using converter system */ if(!ipatch_convert_objects(G_OBJECT(sf2), G_OBJECT(sffile), &err)) { fprintf(stderr, "Failed to save SoundFont to file: %s\n", ipatch_gerror_message(err)); g_clear_error(&err); } g_object_unref(sffile); /* -- unref SoundFont file */ g_object_unref(sf2); /* -- unref SoundFont object */ return (0); /* we done, yeah! :) */ } libinstpatch-1.1.6/examples/create_sf2.py000066400000000000000000000032571400263525300204170ustar00rootroot00000000000000#!/usr/bin/python # Python example of creating a SoundFont from multiple audio samples # # Updated on July 22, 2006 # Now uses more convenient converter functions. # # Josh Green - Sep 13, 2004 # Use this example as you please (public domain) # # Note that the Python binding API is not stable yet and may change # import ipatch import gobject, sys if len (sys.argv[1:]) == 0: print "Usage: create_sf2.py sample1.wav [sample2.wav sample3.aiff ...]" sys.exit () sf2 = ipatch.SF2 () # Create new SoundFont object # For each file specified on command line.. for fname in sys.argv[1:]: # Identify and open the file f = ipatch.ipatch_file_identify_open (fname, "r") if not f: print "Failed to identify file '%s'" % fname continue # Convert sample file to SF2Sample object sample = ipatch.ipatch_convert_object_to_type (f, ipatch.SF2Sample) if not sample: continue sf2.add_unique (sample) # Add sample and ensure name is unique name = sample.get_property ("name") # Get the sample name inst = ipatch.SF2Inst () # Create new SoundFont instrument inst.set_property ("name", name) # Set the instrument name = sample name inst.new_zone (sample) # Add new zone to inst => sample sf2.add_unique (inst) # Add inst and ensure name is unique preset = ipatch.SF2Preset () # Create new preset preset.set_property ("name", name) # Set preset name = sample name preset.new_zone (inst) # Add zone to preset => inst sf2.add_unique (preset) # Add preset, name/bank/preset # unique # Create SoundFont file object, set its name and open for writing sffile = ipatch.SF2File () sffile.open ("output.sf2", "w") ipatch.ipatch_convert_objects (sf2, sffile) sffile.close () libinstpatch-1.1.6/examples/split_sfont.c000066400000000000000000000250751400263525300205420ustar00rootroot00000000000000/* * An example of using libInstPatch. * Splits SoundFonts into multiple SoundFont files, one for each preset. * The files are named . * * Public domain use as you please * * Joshua "Element" Green - Dec 12, 2003 */ #include #include #include #include /* check if an error is set, if it is then use its message, otherwise note that there is no detailed error message */ #define ERROR_MSG(err) (err ? err->message : "") static void dup_sfont_properties(IpatchSF2 *src, IpatchSF2 *dest); static IpatchSF2Preset *recursive_dup_preset(IpatchSF2 *sf, IpatchSF2Preset *src, GError **err); int main(int argc, char *argv[]) { IpatchFile *file; /* file object */ IpatchSF2File *new_file; /* new SoundFont file object */ IpatchSF2 *sfont, *newsf; /* source and destination SoundFont objects */ IpatchSF2Writer *sf2writer; /* SoundFont writer object */ IpatchSF2Preset *preset; /* SoundFont preset pointer */ IpatchSF2Preset *new_preset; /* new preset */ IpatchList *list; /* an item list object */ IpatchIter iter; /* an iterator structure (on the stack) */ IpatchItem *item; /* generic item pointer */ char *src_filename; /* file name of source SoundFont */ char *s, *s2; GError *err = NULL; int i; if(argc < 2) { fprintf(stderr, "Usage: %s [FILES]\n", argv[0]); fprintf(stderr, "Splits SoundFont files into individual presets\n"); exit(1); } ipatch_init(); /* must initialize libInstPatch first */ for(i = 1; i < argc; i++) /* loop over file names */ { file = ipatch_file_new(); /* ++ ref new file */ src_filename = argv[i]; /* open the file in read mode */ if(!ipatch_file_open(file, src_filename, "r", &err)) { fprintf(stderr, "Failed to open file '%s': %s\n", src_filename, ERROR_MSG(err)); g_clear_error(&err); /* free the error information */ g_object_unref(file); /* -- unref the file to free it */ continue; } /* check if its a SoundFont file */ if(ipatch_file_identify(file, &err) != IPATCH_TYPE_SF2_FILE) { if(err) { fprintf(stderr, "Error while identifying file '%s': %s\n", src_filename, ERROR_MSG(err)); g_clear_error(&err); } else fprintf(stderr, "File '%s' is not a SoundFont\n", src_filename); g_object_unref(file); continue; } /* load the SoundFont file to an object */ if(!(item = ipatch_file_load_object(file, &err))) /* ++ ref sfont */ { fprintf(stderr, "Failed to load SoundFont file '%s': %s\n", src_filename, ERROR_MSG(err)); g_clear_error(&err); g_object_unref(file); continue; } sfont = IPATCH_SF2(item); /* type cast the item to a SoundFont */ g_object_unref(file); /* -- unref file, we are done with it */ /* ++ ref new list of preset children */ list = ipatch_container_get_children(IPATCH_CONTAINER(sfont), IPATCH_TYPE_SF2_PRESET); ipatch_list_init_iter(list, &iter); /* initialize iterator to list */ preset = ipatch_sf2_preset_first(&iter); /* first preset */ /* loop over SoundFont presets */ for(; preset; preset = ipatch_sf2_preset_next(&iter)) { newsf = ipatch_sf2_new(); /* ++ ref new destination SoundFont */ dup_sfont_properties(sfont, newsf); /* duplicate the properties */ /* duplicate the preset (++ ref duplicate) */ if(!(new_preset = recursive_dup_preset(newsf, preset, &err))) { fprintf(stderr, "Failed to duplicate preset: %s\n", ERROR_MSG(err)); g_clear_error(&err); g_object_unref(newsf); continue; } /* set bank and program # to 0 */ g_object_set(new_preset, "bank", 0, "program", 0, NULL); /* add the new preset to the new SoundFont */ ipatch_container_add(IPATCH_CONTAINER(newsf), IPATCH_ITEM(new_preset)); g_object_unref(new_preset); /* -- unref new preset */ /* create new SoundFont file name -.sf2 */ g_object_get(preset, "name", &s, NULL); /* get preset name */ s2 = g_strconcat(src_filename, "-", s, ".sf2", NULL); g_free(s); /* create new SoundFont file and set its name */ new_file = ipatch_sf2_file_new(); /* ++ ref new file */ if(!ipatch_file_open(IPATCH_FILE(new_file), s2, "w", &err)) { fprintf(stderr, "Failed to open file '%s' for writing: %s\n", s2, ERROR_MSG(err)); g_clear_error(&err); g_free(s2); g_object_unref(new_file); g_object_unref(newsf); continue; } /* save the new SoundFont file */ sf2writer = ipatch_sf2_writer_new(newsf, new_file); if(!ipatch_sf2_write(sf2writer, &err)) { fprintf(stderr, "Failed to save new SoundFont '%s': %s\n", s2, ERROR_MSG(err)); g_clear_error(&err); } g_object_unref(sf2writer); g_free(s2); g_object_unref(new_file); g_object_unref(newsf); } g_object_unref(list); /* -- unref the list we are done with it */ g_object_unref(sfont); /* -- unref source SoundFont */ } /* for each file */ } /* duplicate text information between 2 SoundFont files (name, author, engine Version, etc) */ static void dup_sfont_properties(IpatchSF2 *src, IpatchSF2 *dest) { GParamSpec **props, **p; /* array of properties and an index pointer */ GParamSpec *pspec; char *s; props = g_object_class_list_properties(G_OBJECT_GET_CLASS(src), NULL); for(p = props; *p; p++) /* loop over all properties */ { pspec = *p; /* string property? */ if(G_PARAM_SPEC_VALUE_TYPE(pspec) == G_TYPE_STRING) { g_object_get(src, pspec->name, &s, NULL); /* get the value */ /* if the value is set then set it on the destination sfont */ if(s) { g_object_set(dest, pspec->name, s, NULL); g_free(s); /* free the value string */ } } } } /* YUCK! This is the messy part, need to come up with some nicer API for duplicating objects between toplevel patch files, this mess is also seen in the Swami paste routines */ /* recursively duplicate a SoundFont preset (i.e., the preset, its instruments and the samples of those instruments */ static IpatchSF2Preset * recursive_dup_preset(IpatchSF2 *sf, IpatchSF2Preset *src, GError **err) { IpatchItem *dup_preset; IpatchSF2Zone *pzone, *izone; IpatchItem *inst, *dup_inst, *sample, *dup_sample; IpatchList *pzone_list, *izone_list; IpatchIter pzone_iter, izone_iter; /* duplicate the preset (still references insts from old sfont) */ if(!(dup_preset = ipatch_item_duplicate(IPATCH_ITEM(src), NULL, err))) { return (NULL); } /* ++ ref new list */ pzone_list = ipatch_container_get_children(IPATCH_CONTAINER(dup_preset), IPATCH_TYPE_SF2_PZONE); ipatch_list_init_iter(pzone_list, &pzone_iter); pzone = ipatch_sf2_zone_first(&pzone_iter); while(pzone) /* loop over preset zones */ { /* ++ ref the preset zone's instrument */ inst = ipatch_sf2_zone_ref_item(pzone); if(inst) /* not a global preset zone? */ { /* ++ ref new duplicate instrument */ if(!(dup_inst = ipatch_item_duplicate(inst, NULL, err))) { g_object_unref(inst); g_object_unref(pzone_list); g_object_unref(dup_preset); return (NULL); } g_object_unref(inst); /* don't need inst no more */ /* point preset zone to the new instrument */ ipatch_sf2_zone_set_item(pzone, dup_inst); /* add the instrument to the SoundFont */ ipatch_container_add(IPATCH_CONTAINER(sf), IPATCH_ITEM(dup_inst)); izone_list = /* ++ ref new list */ ipatch_container_get_children(IPATCH_CONTAINER(dup_inst), IPATCH_TYPE_SF2_IZONE); ipatch_list_init_iter(izone_list, &izone_iter); izone = ipatch_sf2_zone_first(&izone_iter); while(izone) /* loop over instrument zones */ { /* ++ ref instrument zone's sample */ sample = ipatch_sf2_zone_ref_item(izone); if(sample) /* not a global zone? */ { /* ++ ref new duplicate sample */ if(!(dup_sample = ipatch_item_duplicate(sample, NULL, err))) { g_object_unref(izone_list); g_object_unref(dup_inst); g_object_unref(pzone_list); g_object_unref(dup_preset); return (NULL); } g_object_unref(sample); /* -- don't need sample no more */ /* point instrument zone to the new sample */ ipatch_sf2_zone_set_item(izone, dup_sample); /* add the sample to the SoundFont */ ipatch_container_add(IPATCH_CONTAINER(sf), IPATCH_ITEM(dup_sample)); g_object_unref(dup_sample); /* -- unref dup sample */ } izone = ipatch_sf2_zone_next(&izone_iter); } /* while instrument zones */ g_object_unref(izone_list); /* -- unref instrument zone list */ g_object_unref(dup_inst); /* -- unref duplicate instrument */ } /* if not global preset zone */ pzone = ipatch_sf2_zone_next(&pzone_iter); } g_object_unref(pzone_list); /* -- unref preset zone list */ return (IPATCH_SF2_PRESET(dup_preset)); /* !! caller takes over reference */ } libinstpatch-1.1.6/libinstpatch-1.0.pc.in000066400000000000000000000004421400263525300201140ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libInstPatch Description: MIDI instrument patch library Version: @VERSION@ Requires: glib-2.0 gobject-2.0 gthread-2.0 sndfile Libs: -L${libdir} -linstpatch-@lib_version_suffix@ Cflags: -I${includedir} libinstpatch-1.1.6/libinstpatch/000077500000000000000000000000001400263525300166675ustar00rootroot00000000000000libinstpatch-1.1.6/libinstpatch/CMakeLists.txt000066400000000000000000000273111400263525300214330ustar00rootroot00000000000000# # libInstPatch # # Copyright (C) 1999-2014 Element Green # # See COPYING license file for distribution details # include_directories ( ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${GOBJECT_INCLUDEDIR} ${GOBJECT_INCLUDE_DIRS} ${SNDFILE_INCLUDEDIR} ${SNDFILE_INCLUDE_DIRS} ) # ************ library ************ set ( instpatch_public_HEADERS builtin_enums.h compat.h IpatchBase.h IpatchContainer.h IpatchConverter.h IpatchConverterSF2VoiceCache.h IpatchConvert_DLS2.h IpatchConvert_Gig.h IpatchConvert_SF2.h IpatchConvert_SLI.h IpatchDLS2Conn.h IpatchDLS2.h IpatchDLS2Info.h IpatchDLS2Inst.h IpatchDLS2Region.h IpatchDLS2Sample.h IpatchDLSFile.h IpatchDLSReader.h IpatchDLSWriter.h IpatchFile.h IpatchGig.h IpatchGigDimension.h IpatchGigEffects.h IpatchGigFile.h IpatchGigInst.h IpatchGigRegion.h IpatchGigSample.h IpatchGigSubRegion.h IpatchItem.h IpatchIter.h IpatchList.h IpatchParamProp.h IpatchPaste.h IpatchRange.h IpatchRiff.h IpatchSample.h IpatchSampleData.h IpatchSampleList.h IpatchSampleStore.h IpatchSampleStoreCache.h IpatchSampleStoreFile.h IpatchSampleStoreRam.h IpatchSampleStoreRom.h IpatchSampleStoreSndFile.h IpatchSampleStoreSplit24.h IpatchSampleStoreSwap.h IpatchSampleStoreVirtual.h IpatchSampleTransform.h IpatchSF2.h IpatchSF2File.h IpatchSF2Gen.h IpatchSF2GenItem.h IpatchSF2Inst.h IpatchSF2IZone.h IpatchSF2Mod.h IpatchSF2ModItem.h IpatchSF2ModList.h IpatchSF2Preset.h IpatchSF2PZone.h IpatchSF2Reader.h IpatchSF2Sample.h IpatchSF2VoiceCache.h IpatchSF2VoiceCache_DLS.h IpatchSF2VoiceCache_SF2.h IpatchSF2VoiceCache_SLI.h IpatchSF2VoiceCache_Gig.h IpatchSF2VoiceCache_VBank.h IpatchSF2Writer.h IpatchSF2Zone.h IpatchSLI.h IpatchSLIFile.h IpatchSLIInst.h IpatchSLIReader.h IpatchSLISample.h IpatchSLIWriter.h IpatchSLIZone.h IpatchSndFile.h IpatchTypeProp.h IpatchUnit.h IpatchUnit_generic.h IpatchUnit_DLS.h IpatchUnit_SF2.h IpatchVBank.h IpatchVBankInst.h IpatchVBankRegion.h IpatchVirtualContainer.h IpatchVirtualContainer_types.h IpatchXml.h IpatchXmlObject.h misc.h sample.h util.h ) set ( instpatch_private_HEADERS IpatchDLSFile_priv.h IpatchGigFile_priv.h IpatchSF2File_priv.h IpatchSLIFile_priv.h compat.h ) set ( instpatch_SOURCES builtin_enums.c IpatchBase.c IpatchContainer.c IpatchContainer_notify.c IpatchConverter.c IpatchConverterSF2VoiceCache.c IpatchConverter_priv.h IpatchConvert_DLS2.c IpatchConvert_Gig.c IpatchConvert_SF2.c IpatchConvert_SLI.c IpatchDLS2.c IpatchDLS2Conn.c IpatchDLS2Info.c IpatchDLS2Inst.c IpatchDLS2Region.c IpatchDLS2Sample.c IpatchDLSFile.c IpatchDLSReader.c IpatchDLSWriter.c IpatchFile.c IpatchFileBuf.c IpatchGig.c IpatchGigDimension.c IpatchGigEffects.c IpatchGigFile.c IpatchGigInst.c IpatchGigRegion.c IpatchGigSample.c IpatchGigSubRegion.c IpatchItem.c IpatchItemProp.c IpatchIter.c IpatchList.c IpatchParamProp.c IpatchPaste.c IpatchRange.c IpatchRiff.c IpatchSample.c IpatchSampleData.c IpatchSampleList.c IpatchSampleStore.c IpatchSampleStoreCache.c IpatchSampleStoreFile.c IpatchSampleStoreRam.c IpatchSampleStoreRom.c IpatchSampleStoreSndFile.c IpatchSampleStoreSplit24.c IpatchSampleStoreSwap.c IpatchSampleStoreVirtual.c IpatchSampleTransform.c IpatchSF2.c IpatchSF2File.c IpatchSF2Gen.c IpatchSF2GenItem.c IpatchSF2Gen_tables.c IpatchSF2Inst.c IpatchSF2IZone.c IpatchSF2Mod.c IpatchSF2ModItem.c IpatchSF2ModList.c IpatchSF2Preset.c IpatchSF2PZone.c IpatchSF2Reader.c IpatchSF2Sample.c IpatchSF2VoiceCache.c IpatchSF2VoiceCache_DLS.c IpatchSF2VoiceCache_SF2.c IpatchSF2VoiceCache_SLI.c IpatchSF2VoiceCache_Gig.c IpatchSF2VoiceCache_VBank.c IpatchSF2Writer.c IpatchSF2Zone.c IpatchSLI.c IpatchSLIFile.c IpatchSLIInst.c IpatchSLIInst_CatMaps.c IpatchSLIReader.c IpatchSLISample.c IpatchSLIWriter.c IpatchSLIZone.c IpatchSndFile.c IpatchTypeProp.c IpatchUnit.c IpatchUnit_generic.c IpatchUnit_DLS.c IpatchUnit_SF2.c IpatchVBank.c IpatchVBankInst.c IpatchVBankRegion.c IpatchVirtualContainer.c IpatchVirtualContainer_types.c IpatchXml.c IpatchXmlObject.c compat.c i18n.h md5.c md5.h misc.c sample.c util.c ) set ( public_main_HEADER ${CMAKE_CURRENT_BINARY_DIR}/libinstpatch.h ) link_directories ( ${GOBJECT_LIBDIR} ${GOBJECT_LIBRARY_DIRS} ${SNDFILE_LIBDIR} ${SNDFILE_LIBRARY_DIRS} ) add_definitions ( -DLOCALEDIR="${DATA_INSTALL_DIR}/locale" ) set (DEFINITION_FILE "") # Options for WINDOWS only if( WIN32 ) # disable deprecation warnings add_definitions ( -D_CRT_SECURE_NO_WARNINGS ) if (BUILD_SHARED_LIBS) # adding a module definition file for shared libs will export symbols and produce import library set (DEFINITION_FILE libinstpatch.def) endif(BUILD_SHARED_LIBS) endif( WIN32 ) add_library ( libinstpatch ${CMAKE_CURRENT_BINARY_DIR}/marshals.c ${CMAKE_CURRENT_BINARY_DIR}/version.h ${instpatch_SOURCES} ${DEFINITION_FILE} ipatch_priv.h ) target_link_libraries ( libinstpatch ${GOBJECT_LIBRARIES} ${SNDFILE_LIBRARIES} ${IPATCH_LIBS} ) install ( FILES ${instpatch_public_HEADERS} ${public_main_HEADER} ${CMAKE_CURRENT_BINARY_DIR}/version.h DESTINATION ${INCLUDE_INSTALL_DIR}/${INSTPATCH_INSTALL_TARGET}/libinstpatch) find_program (GLIB2_MKENUMS glib-mkenums) find_program (SED sed) add_custom_target(enums COMMAND ${GLIB2_MKENUMS} --fhead \"\#ifndef __IPATCH_BUILTIN_ENUMS_H__\\n\" --fhead \"\#define __IPATCH_BUILTIN_ENUMS_H__\\n\\n\" --fhead \"\#include \\n\\n\" --fhead \"G_BEGIN_DECLS\\n\" --fprod \"/* enumerations from \\"@filename@\\" */\\n\" --vhead \"GType @enum_name@_get_type \(void\)\;\\n\" --vhead \"\#define IPATCH_TYPE_@ENUMSHORT@ \(@enum_name@_get_type\(\)\)\\n\" --ftail \"G_END_DECLS\\n\\n\" --ftail \"\#endif /* __IPATCH_BUILTIN_ENUMS_H__ */\" ${instpatch_public_HEADERS} | ${SED} 's/dl_s2/dls2/g\;s/DL_S2/DLS2/g' > ${CMAKE_CURRENT_BINARY_DIR}/builtin_enums.h COMMAND ${GLIB2_MKENUMS} --fhead \"\#include \\"libinstpatch.h\\"\\n\" --fhead \"\#include \\"ipatch_priv.h\\"\\n\\n\" --fprod \"/* enumerations from \\"@filename@\\" */\" --vhead \"static const G@Type@Value _@enum_name@_values[] = {\" --vprod \" { @VALUENAME@, \\"@VALUENAME@\\", \\"@valuenick@\\" },\" --vtail \" { 0, NULL, NULL }\\n}\;\\n\\n\" --vtail \"GType\\n@enum_name@_get_type \(void\)\\n{\\n\" --vtail \" static GType type = 0\;\\n\\n\" --vtail \" if \(G_UNLIKELY \(type == 0\)\)\\n\" --vtail \" type = g_\@type\@_register_static \(\\"@EnumName@\\", _@enum_name@_values\)\;\\n\\n\" --vtail \" return type\;\\n}\\n\\n\" ${instpatch_public_HEADERS} | ${SED} 's/dl_s2/dls2/g\;s/DL_S2/DLS2/g' > ${CMAKE_CURRENT_BINARY_DIR}/builtin_enums.c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${instpatch_public_HEADERS} ) find_program (GLIB2_GENMARSHAL glib-genmarshal) add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/marshals.c COMMAND ${GLIB2_GENMARSHAL} ARGS --body --prefix=ipatch_marshal ${CMAKE_CURRENT_SOURCE_DIR}/marshals.list > ${CMAKE_CURRENT_BINARY_DIR}/marshals.c WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/marshals.list ${CMAKE_CURRENT_BINARY_DIR}/marshals.h ) add_custom_command ( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/marshals.h COMMAND ${GLIB2_GENMARSHAL} ARGS --header --prefix=ipatch_marshal ${CMAKE_CURRENT_SOURCE_DIR}/marshals.list > ${CMAKE_CURRENT_BINARY_DIR}/marshals.h WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/marshals.list ) if ( MACOSX_FRAMEWORK ) set_property ( SOURCE ${instpatch_public_HEADERS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/libinstpatch ) set_target_properties ( libinstpatch PROPERTIES OUTPUT_NAME "libinstpatch-1.0" FRAMEWORK TRUE PUBLIC_HEADER "${public_main_HEADER}" FRAMEWORK_VERSION "${LIB_VERSION_CURRENT}" INSTALL_NAME_DIR ${FRAMEWORK_INSTALL_DIR} VERSION ${LIB_VERSION_INFO} SOVERSION ${LIB_VERSION_CURRENT} ) elseif( MINGW OR WIN32 ) set_target_properties ( libinstpatch PROPERTIES ARCHIVE_OUTPUT_NAME "instpatch-${LIB_VERSION_CURRENT}" PREFIX "lib" OUTPUT_NAME "instpatch-${LIB_VERSION_CURRENT}" VERSION ${LIB_VERSION_INFO} ) else( MINGW OR WIN32 ) set_target_properties ( libinstpatch PROPERTIES OUTPUT_NAME "instpatch-1.0" VERSION ${LIB_VERSION_INFO} SOVERSION ${LIB_VERSION_CURRENT} ) endif ( MACOSX_FRAMEWORK ) if ( IPATCH_CPPFLAGS ) set_target_properties ( libinstpatch PROPERTIES COMPILE_FLAGS ${IPATCH_CPPFLAGS} ) endif ( IPATCH_CPPFLAGS ) install ( TARGETS libinstpatch RUNTIME DESTINATION ${BIN_INSTALL_DIR} LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR} FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR} BUNDLE DESTINATION ${BUNDLE_INSTALL_DIR} ) if ( MSVC ) install(FILES $ DESTINATION ${BIN_INSTALL_DIR} OPTIONAL) endif ( MSVC ) macro(_list_prefix _outvar _listvar _prefix) set(${_outvar}) foreach(_item IN LISTS ${_listvar}) list(APPEND ${_outvar} ${_prefix}${_item}) endforeach() endmacro(_list_prefix) # GObject Introspection if (INTROSPECTION_FOUND) # libInstPatch has to be initialized, so we build an introspection program which does this add_executable ( gir_prog gir_prog.c ) target_link_libraries ( gir_prog libinstpatch ${GOBJECT_LIBRARIES} ${SNDFILE_LIBRARIES} ${IPATCH_LIBS} ${INTROSPECTION_LIBRARIES} ) string (REPLACE ";" " " INTROSPECTION_CFLAGS_STR "${INTROSPECTION_CFLAGS}") set_target_properties (gir_prog PROPERTIES COMPILE_FLAGS "${INTROSPECTION_CFLAGS_STR}") include(GObjectIntrospectionMacros) set(INTROSPECTION_GIRS) set(INTROSPECTION_SCANNER_ARGS "--add-include-path=${CMAKE_CURRENT_SOURCE_DIR}") set(INTROSPECTION_COMPILER_ARGS "--includedir=${CMAKE_CURRENT_SOURCE_DIR}") set(introspection_files ${instpatch_SOURCES} ${instpatch_public_HEADERS}) set(Ipatch_1_1_gir ${INSTPATCH_INSTALL_TARGET}) set(Ipatch_1_1_gir_INCLUDES GObject-2.0) set(Ipatch_1_1_gir_LIBS ${INSTPATCH_INSTALL_TARGET}) get_directory_property(_tmp_includes INCLUDE_DIRECTORIES) _list_prefix(_includes _tmp_includes "-I") set(Ipatch_1_1_gir_CFLAGS ${_includes}) set(Ipatch_1_1_gir_PROGRAM ${CMAKE_CURRENT_BINARY_DIR}/gir_prog) _list_prefix(_abs_introspection_files introspection_files "${CMAKE_CURRENT_SOURCE_DIR}/") list(APPEND _abs_introspection_files ${CMAKE_CURRENT_SOURCE_DIR}/builtin_enums.c ${CMAKE_CURRENT_SOURCE_DIR}/builtin_enums.h ${CMAKE_CURRENT_BINARY_DIR}/version.h ) set(Ipatch_1_1_gir_NAMESPACE Ipatch) set(Ipatch_1_1_gir_VERSION 1.1) set(Ipatch_1_1_gir_FILES ${_abs_introspection_files}) set(Ipatch_1_1_gir_SCANNERFLAGS --c-include ${public_main_HEADER}) list(APPEND INTROSPECTION_GIRS Ipatch-1.1.gir) gir_add_introspections(INTROSPECTION_GIRS) add_dependencies (gir-girs gir_prog) # Add introspection program as a dependency of gir-girs endif (INTROSPECTION_FOUND) libinstpatch-1.1.6/libinstpatch/IpatchBase.c000066400000000000000000000644661400263525300210560ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchBase * @short_description: Base instrument file object type * @see_also: * @stability: Stable * * Defines an abstract object type which is used as the basis of instrument * files, such as #IpatchSF2, #IpatchDLS, etc. */ #include #include #include #include #include #include #include #include "IpatchBase.h" #include "IpatchConverter.h" #include "IpatchParamProp.h" #include "IpatchTypeProp.h" #include "util.h" #include "ipatch_priv.h" enum { PROP_0, PROP_CHANGED, PROP_SAVED, PROP_FILENAME, PROP_FILE }; static void ipatch_base_finalize(GObject *gobject); static void ipatch_base_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_base_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_base_real_set_file(IpatchBase *base, IpatchFile *file); static void ipatch_base_real_set_file_name(IpatchBase *base, const char *file_name); static gboolean ipatch_base_real_save(IpatchBase *base, const char *filename, gboolean save_a_copy, GError **err); /* private var used by IpatchItem, for fast "changed" property notifies */ GParamSpec *ipatch_base_pspec_changed; /* cached parameter specs to speed up prop notifies */ static GParamSpec *file_pspec; static GParamSpec *file_name_pspec; G_DEFINE_ABSTRACT_TYPE(IpatchBase, ipatch_base, IPATCH_TYPE_CONTAINER) /** * ipatch_base_type_get_mime_type: * @base_type: #IpatchBase derived type to get mime type of * * Get the mime type of the file type associated with the given base patch file type. * * Returns: Mime type or NULL if none assigned for this base type. * Free the string when finished with it. * * Since: 1.1.0 */ char * ipatch_base_type_get_mime_type(GType base_type) { const IpatchConverterInfo *info; char *mime_type; info = ipatch_lookup_converter_info(0, base_type, IPATCH_TYPE_FILE); if(!info) { return (NULL); } ipatch_type_get(info->dest_type, "mime-type", &mime_type, NULL); return (mime_type); } static void ipatch_base_class_init(IpatchBaseClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); item_class->item_set_property = ipatch_base_set_property; obj_class->get_property = ipatch_base_get_property; obj_class->finalize = ipatch_base_finalize; ipatch_base_pspec_changed = g_param_spec_boolean("changed", "Changed", "Changed Flag", TRUE, G_PARAM_READWRITE | IPATCH_PARAM_NO_SAVE_CHANGE | IPATCH_PARAM_NO_SAVE); g_object_class_install_property(obj_class, PROP_CHANGED, ipatch_base_pspec_changed); g_object_class_install_property(obj_class, PROP_SAVED, g_param_spec_boolean("saved", "Saved", "Been Saved Flag", FALSE, G_PARAM_READWRITE | IPATCH_PARAM_NO_SAVE_CHANGE | IPATCH_PARAM_NO_SAVE)); file_name_pspec = g_param_spec_string("file-name", "File Name", "File Name", "untitled", G_PARAM_READWRITE | IPATCH_PARAM_NO_SAVE_CHANGE); g_object_class_install_property(obj_class, PROP_FILENAME, file_name_pspec); file_pspec = g_param_spec_object("file", "File", "File Object", IPATCH_TYPE_FILE, G_PARAM_READWRITE | IPATCH_PARAM_NO_SAVE | IPATCH_PARAM_HIDE | IPATCH_PARAM_NO_SAVE_CHANGE); g_object_class_install_property(obj_class, PROP_FILE, file_pspec); } static void ipatch_base_init(IpatchBase *base) { } /* function called when a patch is being destroyed */ static void ipatch_base_finalize(GObject *gobject) { IpatchBase *base = IPATCH_BASE(gobject); IPATCH_ITEM_WLOCK(base); if(base->file) { ipatch_file_unref_from_object(base->file, gobject); // -- unref file from object } base->file = NULL; IPATCH_ITEM_WUNLOCK(base); if(G_OBJECT_CLASS(ipatch_base_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_base_parent_class)->finalize(gobject); } } static void ipatch_base_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchBase *base = IPATCH_BASE(object); switch(property_id) { case PROP_CHANGED: if(g_value_get_boolean(value)) { ipatch_item_set_flags(IPATCH_ITEM(base), IPATCH_BASE_CHANGED); } else { ipatch_item_clear_flags(IPATCH_ITEM(base), IPATCH_BASE_CHANGED); } break; case PROP_SAVED: if(g_value_get_boolean(value)) { ipatch_item_set_flags(IPATCH_ITEM(base), IPATCH_BASE_SAVED); } else { ipatch_item_clear_flags(IPATCH_ITEM(base), IPATCH_BASE_SAVED); } break; case PROP_FILENAME: ipatch_base_real_set_file_name(base, g_value_get_string(value)); break; case PROP_FILE: ipatch_base_real_set_file(base, g_value_get_object(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_base_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchBase *base; g_return_if_fail(IPATCH_IS_BASE(object)); base = IPATCH_BASE(object); switch(property_id) { case PROP_CHANGED: g_value_set_boolean(value, ipatch_item_get_flags(IPATCH_ITEM(base)) & IPATCH_BASE_CHANGED); break; case PROP_SAVED: g_value_set_boolean(value, ipatch_item_get_flags(IPATCH_ITEM(base)) & IPATCH_BASE_SAVED); break; case PROP_FILENAME: g_value_take_string(value, ipatch_base_get_file_name(base)); break; case PROP_FILE: g_value_take_object(value, ipatch_base_get_file(base)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /** * ipatch_base_set_file: * @base: Patch base object to set file object of * @file: File object * * Sets the file object associated with a patch. */ void ipatch_base_set_file(IpatchBase *base, IpatchFile *file) { GValue value = { 0 }, oldval = { 0 }; g_return_if_fail(IPATCH_IS_BASE(base)); g_return_if_fail(IPATCH_IS_FILE(file)); g_value_init(&value, IPATCH_TYPE_FILE); g_value_set_object(&value, file); ipatch_item_get_property_fast((IpatchItem *)base, file_pspec, &oldval); ipatch_base_real_set_file(base, file); ipatch_item_prop_notify((IpatchItem *)base, file_pspec, &value, &oldval); g_value_unset(&value); g_value_unset(&oldval); } static void ipatch_base_real_set_file(IpatchBase *base, IpatchFile *file) { GValue value = { 0 }, oldval = { 0 }; IpatchFile *oldfile; ipatch_file_ref_from_object(file, (GObject *)base); // ++ ref new file from object IPATCH_ITEM_WLOCK(base); oldfile = base->file; base->file = file; IPATCH_ITEM_WUNLOCK(base); g_value_init(&oldval, G_TYPE_STRING); if(oldfile) { g_value_take_string(&oldval, ipatch_file_get_name(oldfile)); ipatch_file_unref_from_object(oldfile, (GObject *)base); // -- remove reference to old file } g_value_init(&value, G_TYPE_STRING); g_value_take_string(&value, ipatch_file_get_name(file)); // Notify file-name property change as well ipatch_item_prop_notify((IpatchItem *)base, file_name_pspec, &value, &oldval); g_value_unset(&value); g_value_unset(&oldval); } /** * ipatch_base_get_file: * @base: Patch base object to get file object from * * Get the file object associated with a patch base object. Caller owns a * reference to the returned file object and it should be unrefed when * done with it. * * Returns: (transfer full): File object or %NULL if not set. * Remember to unref it when done with it. */ IpatchFile * ipatch_base_get_file(IpatchBase *base) { IpatchFile *file; g_return_val_if_fail(IPATCH_IS_BASE(base), NULL); IPATCH_ITEM_RLOCK(base); file = base->file; if(file) { g_object_ref(file); } IPATCH_ITEM_RUNLOCK(base); return (file); } /** * ipatch_base_set_file_name: * @base: Patch base object to set file name of * @file_name: Path and name to set filename to * * Sets the file name of the file object in a patch base object. File object * should have been set before calling this function (otherwise request is * silently ignored). A convenience function as one could get the file object * and set it directly. */ void ipatch_base_set_file_name(IpatchBase *base, const char *file_name) { GValue value = { 0 }, oldval = { 0 }; g_return_if_fail(IPATCH_IS_BASE(base)); g_value_init(&value, G_TYPE_STRING); g_value_set_string(&value, file_name); ipatch_item_get_property_fast((IpatchItem *)base, file_name_pspec, &oldval); ipatch_base_real_set_file_name(base, file_name); ipatch_item_prop_notify((IpatchItem *)base, file_name_pspec, &value, &oldval); g_value_unset(&value); g_value_unset(&oldval); } /* the real set file name routine, user routine does a notify */ static void ipatch_base_real_set_file_name(IpatchBase *base, const char *file_name) { IPATCH_ITEM_RLOCK(base); if(!base->file) /* silently fail */ { IPATCH_ITEM_RUNLOCK(base); return; } ipatch_file_set_name(base->file, file_name); IPATCH_ITEM_RUNLOCK(base); } /** * ipatch_base_get_file_name: * @base: Base patch item to get file name from. * * Get the file name of the file object in a base patch item. A convenience * function. * * Returns: New allocated file name or %NULL if not set or file object * not set. String should be freed when finished with it. */ char * ipatch_base_get_file_name(IpatchBase *base) { char *file_name = NULL; g_return_val_if_fail(IPATCH_IS_BASE(base), NULL); IPATCH_ITEM_RLOCK(base); if(base->file) { file_name = ipatch_file_get_name(base->file); } IPATCH_ITEM_RUNLOCK(base); return (file_name); } /** * ipatch_base_find_unused_midi_locale: * @base: Patch base object * @bank: (inout): MIDI bank number * @program: (inout): MIDI program number * @exclude: (nullable): Child of @base with MIDI locale to exclude or %NULL * @percussion: Set to %TRUE to find a free percussion MIDI locale * * Finds an unused MIDI locale (bank:program number pair) in a patch * base object. The way in which MIDI bank and program numbers are * used is implementation dependent. Percussion instruments often * affect the bank parameter (for example SoundFont uses bank 128 for * percussion presets). On input the @bank and @program parameters * set the initial locale to start from (set to 0:0 to find the first * free value). If the @percussion parameter is set it may affect * @bank, if its not set, bank will not be modified (e.g., if bank is * a percussion value it will be used). The exclude parameter can be * set to a child item of @base to exclude from the list of "used" * locales (useful when making an item unique that is already parented * to @base). On return @bank and @program will be set to an unused * MIDI locale based on the input criteria. */ void ipatch_base_find_unused_midi_locale(IpatchBase *base, int *bank, int *program, const IpatchItem *exclude, gboolean percussion) { IpatchBaseClass *klass; g_return_if_fail(IPATCH_IS_BASE(base)); g_return_if_fail(bank != NULL); g_return_if_fail(program != NULL); *bank = 0; *program = 0; klass = IPATCH_BASE_GET_CLASS(base); if(klass && klass->find_unused_locale) { klass->find_unused_locale(base, bank, program, exclude, percussion); } } /** * ipatch_base_find_item_by_midi_locale: * @base: Patch base object * @bank: MIDI bank number of item to search for * @program: MIDI program number of item to search for * * Find a child object in a base patch object which matches the given MIDI * locale (@bank and @program numbers). * * Returns: (transfer full): The item or %NULL if not found. The caller owns a reference to the * returned object, and is responsible for unref'ing when finished. */ IpatchItem * ipatch_base_find_item_by_midi_locale(IpatchBase *base, int bank, int program) { IpatchBaseClass *klass; g_return_val_if_fail(IPATCH_IS_BASE(base), NULL); klass = IPATCH_BASE_GET_CLASS(base); if(klass && klass->find_item_by_locale) { return (klass->find_item_by_locale(base, bank, program)); } else { return (NULL); } } /* GFunc used by g_list_foreach() to remove created sample stores */ static void remove_created_stores(gpointer data, gpointer user_data) { IpatchSampleStore *store = data; IpatchSampleData *sampledata; sampledata = (IpatchSampleData *)ipatch_item_get_parent((IpatchItem *)store); // ++ ref parent sampledata if(sampledata) { ipatch_sample_data_remove(sampledata, store); } g_object_unref(sampledata); // -- unref sampledata } /** * ipatch_base_save: * @base: Base item to save * @err: Location to store error info or %NULL * * Save a patch item to the assigned filename or file object. This function handles * saving over an existing file and migrates sample stores as needed. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set) * * Since: 1.1.0 */ gboolean ipatch_base_save(IpatchBase *base, GError **err) { return ipatch_base_real_save(base, NULL, FALSE, err); } /** * ipatch_base_save_to_filename: * @base: Base item to save * @filename: (nullable): New file name to save to or %NULL to use current one * @err: Location to store error info or %NULL * * Save a patch item to a file. This function handles saving over an existing * file and migrates sample stores as needed. It is an error to try to save * over an open file that is not owned by @base. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set) * * Since: 1.1.0 */ gboolean ipatch_base_save_to_filename(IpatchBase *base, const char *filename, GError **err) { return ipatch_base_real_save(base, filename, FALSE, err); } /** * ipatch_base_save_a_copy: * @base: Base item to save * @filename: File name to save a copy to * @err: Location to store error info or %NULL * * Save a patch item to a file name. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set) * * Since: 1.1.0 */ gboolean ipatch_base_save_a_copy(IpatchBase *base, const char *filename, GError **err) { return ipatch_base_real_save(base, filename, TRUE, err); } /* * ipatch_base_real_save: * @base: Base item to save * @filename: New file name to save to or %NULL to use current one * @save_a_copy: If TRUE then new file object will not be assigned * @err: Location to store error info or %NULL * * Save a patch item to a file. This function handles saving over an existing * file and migrates sample stores as needed. It is an error to try to save * over an open file that is not owned by @base though. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set) */ static gboolean ipatch_base_real_save(IpatchBase *base, const char *filename, gboolean save_a_copy, GError **err) { const IpatchConverterInfo *info; IpatchFile *lookup_file, *newfile = NULL, *oldfile = NULL; char *tmp_fname = NULL, *abs_fname = NULL, *base_fname = NULL; IpatchConverter *converter; gboolean tempsave = FALSE; // Set to TRUE if writing to a temp file first, before replacing a file GError *local_err = NULL; IpatchList *created_stores = NULL; int tmpfd; g_return_val_if_fail(IPATCH_IS_BASE(base), FALSE); g_return_val_if_fail(!err || !*err, FALSE); g_object_get(base, "file", &oldfile, NULL); // ++ ref old file (if any) /* Check if file name specified would overwrite another open file */ if(filename) { abs_fname = ipatch_util_abs_filename(filename); // ++ allocate absolute filename lookup_file = ipatch_file_pool_lookup(abs_fname); // ++ ref file matching filename if(lookup_file) { g_object_unref(lookup_file); // -- unref file (we only need the pointer value) } if(lookup_file && lookup_file != oldfile) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_BUSY, _("Refusing to save over other open file '%s'"), abs_fname); goto error; } } if(oldfile) { g_object_get(base, "file-name", &base_fname, NULL); // ++ allocate base file name } // Write to temporary file if saving over or new file name exists tempsave = !abs_fname || (base_fname && strcmp(abs_fname, base_fname) == 0) || g_file_test(abs_fname, G_FILE_TEST_EXISTS); /* if no filename specified try to use current one */ if(!abs_fname) { if(!base_fname) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID, _("File name not supplied and none assigned")); goto error; } abs_fname = base_fname; // !! abs_fname takes over base_fname base_fname = NULL; } else { g_free(base_fname); // -- free base file name } /* Find a converter from base object to file */ info = ipatch_lookup_converter_info(0, G_OBJECT_TYPE(base), IPATCH_TYPE_FILE); if(!info) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNSUPPORTED, _("Saving object of type '%s' to file '%s' not supported"), g_type_name(G_OBJECT_TYPE(base)), abs_fname); goto error; } if(tempsave) // Saving to a temporary file? { tmp_fname = g_strconcat(abs_fname, "_tmpXXXXXX", NULL); // ++ alloc temporary file name // open temporary file in same directory as destination if((tmpfd = g_mkstemp(tmp_fname)) == -1) { g_set_error(err, G_FILE_ERROR, g_file_error_from_errno(errno), _("Unable to open temp file '%s' for writing: %s"), tmp_fname, g_strerror(errno)); goto error; } newfile = IPATCH_FILE(g_object_new(info->dest_type, "file-name", tmp_fname, NULL)); /* ++ ref new file */ ipatch_file_assign_fd(newfile, tmpfd, TRUE); /* Assign file descriptor and set close on finalize */ } else // Not replacing a file, just save it directly without using a temporary file { newfile = IPATCH_FILE(g_object_new(info->dest_type, "file-name", abs_fname, NULL)); /* ++ ref new file */ } // ++ Create new converter and set create-stores property if not "save a copy" mode converter = IPATCH_CONVERTER(g_object_new(info->conv_type, "create-stores", !save_a_copy, NULL)); ipatch_converter_add_input(converter, G_OBJECT(base)); ipatch_converter_add_output(converter, G_OBJECT(newfile)); /* attempt to save patch file */ if(!ipatch_converter_convert(converter, err)) { g_object_unref(converter); // -- unref converter goto error; } // If "create-stores" was set, then we get the list of stores in case of error, so new stores can be removed if(!save_a_copy) { IpatchList *out_list = ipatch_converter_get_outputs(converter); // ++ ref output object list created_stores = (IpatchList *)g_list_nth_data(out_list->items, 1); if(created_stores) { g_object_ref(created_stores); // ++ ref created stores list } g_object_unref(out_list); // -- unref output object list } g_object_unref(converter); // -- unref converter if(tempsave) { ipatch_file_assign_fd(newfile, -1, FALSE); // Unset file descriptor of file (now using file name), closes file descriptor } // Migrate samples if(!save_a_copy) { if(!ipatch_migrate_file_sample_data(oldfile, newfile, abs_fname, IPATCH_SAMPLE_DATA_MIGRATE_REMOVE_NEW_IF_UNUSED | IPATCH_SAMPLE_DATA_MIGRATE_TO_NEWFILE | (tempsave ? IPATCH_SAMPLE_DATA_MIGRATE_REPLACE : 0), err)) { goto error; } ipatch_base_set_file(IPATCH_BASE(base), newfile); // Assign new file to base object } else if(tempsave && !ipatch_file_rename(newfile, abs_fname, err)) // If "save a copy" mode and saved to a temporary file, rename it here { goto error; } if(created_stores) { g_object_unref(created_stores); // -- unref created stores } g_object_unref(newfile); // -- unref creators reference g_free(tmp_fname); // -- free temp file name g_free(abs_fname); // -- free file name if(oldfile) { g_object_unref(oldfile); // -- unref old file } return (TRUE); error: if(created_stores) // Remove new created stores { g_list_foreach(created_stores->items, remove_created_stores, NULL); g_object_unref(created_stores); // -- unref created stores } if(newfile) { if(!ipatch_file_unlink(newfile, &local_err)) // -- Delete new file { g_warning(_("Failed to remove file after save failure: %s"), ipatch_gerror_message(local_err)); g_clear_error(&local_err); } g_object_unref(newfile); // -- unref creators reference } g_free(tmp_fname); // -- free temp file name g_free(abs_fname); // -- free file name if(oldfile) { g_object_unref(oldfile); // -- unref old file } return (FALSE); } /** * ipatch_base_close: * @base: Base item to close * @err: Location to store error info or %NULL * * Close a base instrument object (using ipatch_item_remove()), migrating sample data as needed. * * Returns: TRUE on success, FALSE otherwise (in which case @err may be set) * * Since: 1.1.0 */ gboolean ipatch_base_close(IpatchBase *base, GError **err) { IpatchFile *file; g_return_val_if_fail(IPATCH_IS_BASE(base), FALSE); g_return_val_if_fail(!err || !*err, FALSE); g_object_get(base, "file", &file, NULL); // ++ ref file (if any) ipatch_item_remove(IPATCH_ITEM(base)); if(file && !ipatch_migrate_file_sample_data(file, NULL, NULL, 0, err)) { g_object_unref(file); // -- unref file return (FALSE); } g_object_unref(file); // -- unref file return (TRUE); } /** * ipatch_close_base_list: * @list: List of base objects to close (non base objects are skipped) * @err: Location to store error info or %NULL * * Close a list of base instrument objects (using ipatch_item_remove_recursive()), * migrating sample data as needed. Using this function instead of ipatch_base_close() * can save on unnecessary sample data migrations, if multiple base objects reference * the same sample data. * * Returns: TRUE on success, FALSE otherwise (in which case @err may be set) * * Since: 1.1.0 */ gboolean ipatch_close_base_list(IpatchList *list, GError **err) { GList *p, *file_list = NULL; IpatchFile *file; gboolean retval = TRUE; GError *local_err = NULL; char *filename; g_return_val_if_fail(IPATCH_IS_LIST(list), FALSE); g_return_val_if_fail(!err || !*err, FALSE); for(p = list->items; p; p = p->next) { if(!IPATCH_IS_BASE(p->data)) { continue; // Just skip if its not a base object } g_object_get(p->data, "file", &file, NULL); // ++ ref file (if any) ipatch_item_remove_recursive(IPATCH_ITEM(p->data), TRUE); // Recursively remove to release IpatchSampleData resources if(file) { file_list = g_list_prepend(file_list, file); } } file_list = g_list_reverse(file_list); // Reverse list to migrate samples in same order for(p = file_list; p; p = g_list_delete_link(p, p)) { file = p->data; if(!ipatch_migrate_file_sample_data(file, NULL, NULL, 0, &local_err)) { if(!retval || !err) { // Log additional errors g_object_get(file, "file-name", &filename, NULL); // ++ alloc filename g_critical(_("Error migrating samples from closed file '%s': %s"), filename, ipatch_gerror_message(local_err)); g_free(filename); // -- free filename g_clear_error(&local_err); } else { g_propagate_error(err, local_err); // Propagate first error } retval = FALSE; } g_object_unref(file); // -- unref file } return (retval); } libinstpatch-1.1.6/libinstpatch/IpatchBase.h000066400000000000000000000075571400263525300210610ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. * */ #ifndef __IPATCH_BASE_H__ #define __IPATCH_BASE_H__ #include #include #include /* forward type declarations */ typedef struct _IpatchBase IpatchBase; typedef struct _IpatchBaseClass IpatchBaseClass; #include #include #include #include #include #define IPATCH_TYPE_BASE (ipatch_base_get_type ()) #define IPATCH_BASE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_BASE, IpatchBase)) #define IPATCH_BASE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_BASE, IpatchBaseClass)) #define IPATCH_IS_BASE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_BASE)) #define IPATCH_IS_BASE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_BASE)) #define IPATCH_BASE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_BASE, IpatchBaseClass)) /** * IpatchBaseFlags: * @IPATCH_BASE_CHANGED: Does the base object have any unsaved changes? * @IPATCH_BASE_SAVED: Has the base object ever been saved? */ typedef enum { IPATCH_BASE_CHANGED = 1 << IPATCH_ITEM_UNUSED_FLAG_SHIFT, IPATCH_BASE_SAVED = 1 << (IPATCH_ITEM_UNUSED_FLAG_SHIFT + 1) } IpatchBaseFlags; /* we reserve a couple flags for backwards compatible expansion */ /** * IPATCH_BASE_UNUSED_FLAG_SHIFT: (skip) */ #define IPATCH_BASE_UNUSED_FLAG_SHIFT (IPATCH_ITEM_UNUSED_FLAG_SHIFT + 4) /* patch base object */ struct _IpatchBase { IpatchContainer parent_instance; /* derived from IpatchContainer */ /*< private >*/ IpatchFile *file; /* file object associated with this patch */ }; /* SoundFont class */ struct _IpatchBaseClass { IpatchContainerClass parent_class; /* methods */ void (*find_unused_locale)(IpatchBase *base, int *bank, int *program, const IpatchItem *exclude, gboolean percussion); IpatchItem *(*find_item_by_locale)(IpatchBase *base, int bank, int program); }; /** * IPATCH_BASE_DEFAULT_NAME: (skip) */ #define IPATCH_BASE_DEFAULT_NAME "Untitled" GType ipatch_base_get_type(void); char *ipatch_base_type_get_mime_type(GType base_type); void ipatch_base_set_file(IpatchBase *base, IpatchFile *file); IpatchFile *ipatch_base_get_file(IpatchBase *base); void ipatch_base_set_file_name(IpatchBase *base, const char *file_name); char *ipatch_base_get_file_name(IpatchBase *base); void ipatch_base_find_unused_midi_locale(IpatchBase *base, int *bank, int *program, const IpatchItem *exclude, gboolean percussion); IpatchItem *ipatch_base_find_item_by_midi_locale(IpatchBase *base, int bank, int program); gboolean ipatch_base_save(IpatchBase *base, GError **err); gboolean ipatch_base_save_to_filename(IpatchBase *base, const char *filename, GError **err); gboolean ipatch_base_save_a_copy(IpatchBase *base, const char *filename, GError **err); gboolean ipatch_base_close(IpatchBase *base, GError **err); gboolean ipatch_close_base_list(IpatchList *list, GError **err); #endif libinstpatch-1.1.6/libinstpatch/IpatchContainer.c000066400000000000000000000532321400263525300221130ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchContainer * @short_description: Abstract object type used for items containing other * child items. * @see_also: * @stability: Stable * * Objects which are derived from this abstract type can contain other items, * thus forming a tree of items in an instrument file for example. */ #include #include #include #include "IpatchContainer.h" #include "ipatch_priv.h" /* libInstPatch private functions defined in IpatchContainer_notify.c */ extern void _ipatch_container_notify_init(void); extern void ipatch_container_add_notify(IpatchContainer *container, IpatchItem *child); extern void ipatch_container_remove_notify(IpatchContainer *container, IpatchItem *child); static void ipatch_container_init_class(IpatchContainerClass *klass); static void ipatch_container_dispose(GObject *object); static void ipatch_container_item_remove_full(IpatchItem *item, gboolean full); static GObjectClass *parent_class = NULL; /* for fast hook method access */ static IpatchContainerClass *real_container_class = NULL; GType ipatch_container_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchContainerClass), NULL, NULL, (GClassInitFunc) ipatch_container_init_class, NULL, NULL, sizeof(IpatchContainer), 0, (GInstanceInitFunc) NULL, }; item_type = g_type_register_static(IPATCH_TYPE_ITEM, "IpatchContainer", &item_info, G_TYPE_FLAG_ABSTRACT); } return (item_type); } static void ipatch_container_init_class(IpatchContainerClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); parent_class = g_type_class_peek_parent(klass); real_container_class = klass; obj_class->dispose = ipatch_container_dispose; item_class->remove_full = ipatch_container_item_remove_full; } static void ipatch_container_dispose(GObject *object) { /* clear hooks flag, we are finalizing, shouldn't be any hook callbacks or should there be? FIXME */ ipatch_item_clear_flags(IPATCH_ITEM(object), IPATCH_ITEM_HOOKS_ACTIVE); ipatch_container_remove_all(IPATCH_CONTAINER(object)); if(parent_class->dispose) { parent_class->dispose(object); } } static void ipatch_container_item_remove_full(IpatchItem *item, gboolean full) { if(full) { ipatch_container_remove_all(IPATCH_CONTAINER(item)); } if(IPATCH_ITEM_CLASS(parent_class)->remove_full) { IPATCH_ITEM_CLASS(parent_class)->remove_full(item, full); } } /** * ipatch_container_get_children: (skip) * @container: Container object * @type: GType of child items to get (will match type descendants as well) * * Get a list of child items from a container object. A new #IpatchList is * created containing all matching child items. The returned list object can * be iterated over safely even in a multi-thread environment. If * performance is an issue, ipatch_container_init_iter() can be used instead, * although it requires locking of the container object. * * Returns: (transfer full): New object list containing all matching items (an empty * list if no items matched). The caller owns a reference to the * object list and removing the reference will destroy it. */ IpatchList * ipatch_container_get_children(IpatchContainer *container, GType type) { IpatchList *list; GList *items; items = ipatch_container_get_children_by_type(container, type); list = ipatch_list_new(); list->items = items; return (list); } /** * ipatch_container_get_children_list: (rename-to ipatch_container_get_children) * @container: Container object * * Get a list of all child items from a container object. The returned list object can * be iterated over safely even in a multi-thread environment. * * Returns: (element-type Ipatch.Item) (transfer full) (nullable): New object list containing all child items (an empty * list if no children). Free the list with ipatch_glist_unref_free() when finished using it. * * Since: 1.1.0 */ GList * ipatch_container_get_children_list(IpatchContainer *container) { return ipatch_container_get_children_by_type(container, IPATCH_TYPE_ITEM); } /** * ipatch_container_get_children_by_type: * @container: Container object * @type: GType of child items to get (will match type descendants as well) * * Get a list of child items from a container object. The returned list object can * be iterated over safely even in a multi-thread environment. * * Returns: (element-type Ipatch.Item) (transfer full) (nullable): New object list containing all matching items (an empty * list if no items matched). Free the list with ipatch_glist_unref_free() when finished using it. * * Since: 1.1.0 */ GList * ipatch_container_get_children_by_type(IpatchContainer *container, GType type) { GList *list = NULL; const GType *child_types; IpatchIter iter; GObject *obj; g_return_val_if_fail(IPATCH_IS_CONTAINER(container), NULL); g_return_val_if_fail(g_type_is_a(type, G_TYPE_OBJECT), NULL); /* get container child types */ child_types = ipatch_container_get_child_types(container); while(*child_types) /* loop over child types */ { if(g_type_is_a(*child_types, type)) /* child type matches type? */ { IPATCH_ITEM_RLOCK(container); if(!ipatch_container_init_iter(container, &iter, *child_types)) { ipatch_glist_unref_free(list); IPATCH_ITEM_RUNLOCK(container); return (NULL); } obj = ipatch_iter_first(&iter); while(obj) /* add object list to children list */ { g_object_ref(obj); /* ++ ref object for list */ list = g_list_prepend(list, obj); obj = ipatch_iter_next(&iter); } IPATCH_ITEM_RUNLOCK(container); } child_types++; } return g_list_reverse(list); /* since we prepended */ } /** * ipatch_container_get_child_types: * @container: Container object * * Get an array of child types for a container object. The number of types * is the number of individual lists the container has. * * Returns: (array zero-terminated=1) (transfer none): Pointer to a zero * terminated array of types. Array is static and should not be modified or freed. */ const GType * ipatch_container_get_child_types(IpatchContainer *container) { IpatchContainerClass *klass; g_return_val_if_fail(IPATCH_IS_CONTAINER(container), 0); klass = IPATCH_CONTAINER_GET_CLASS(container); if(klass->child_types) { return(klass->child_types()); } else { return(NULL); } } /** * ipatch_container_get_virtual_types: * @container: Container object * * Get an array of virtual types for a container object. Virtual types are * used to group child items in user interfaces (an example is SoundFont * "Percussion Presets" which contains all presets in bank number 128). To * discover what virtual container a child object is part of lookup it's * "virtual-parent-type" type property (ipatch_type_get() or * ipatch_type_object_get()). * * Returns: (array zero-terminated=1) (transfer none): Pointer to a zero * terminated array of types or %NULL if no virtual types for @container. * Array is static and should not be modified or freed. */ const GType * ipatch_container_get_virtual_types(IpatchContainer *container) { IpatchContainerClass *klass; g_return_val_if_fail(IPATCH_IS_CONTAINER(container), 0); klass = IPATCH_CONTAINER_GET_CLASS(container); if(klass->virtual_types) { return (klass->virtual_types()); } else { return (NULL); } } /** * ipatch_container_type_get_child_types: * @container_type: A #IpatchContainer derived type to get child types of * * Get an array of child types for a container type. The number of types * is the number of individual lists the container has. Like * ipatch_container_get_child_types() but takes a container GType instead of * a container object. * * Returns: (array zero-terminated=1) (transfer none) (nullable): Pointer to a zero * terminated array of types or NULL if none. Array is static and should not be * modified or freed. */ const GType * ipatch_container_type_get_child_types(GType container_type) { IpatchContainerClass *klass; const GType *types; g_return_val_if_fail(g_type_is_a(container_type, IPATCH_TYPE_CONTAINER), NULL); klass = g_type_class_ref(container_type); if(klass->child_types) { types = klass->child_types(); } else { types = NULL; } g_type_class_unref(klass); return (types); } /** * ipatch_container_insert: * @container: Container item to insert into * @item: Item to insert * @pos: Index position to insert @item into (@item type is used to * determine what list to insert into). 0 = first, less than 0 = last. * * Inserts an item into a patch item container. * * MT-NOTE: If position in list is critical the container item should * be locked and ipatch_container_insert_iter() used instead (see other * special requirements for using this function). * Only inserting in the first or last position (@pos is 0 or less than 0) * is guaranteed. */ void ipatch_container_insert(IpatchContainer *container, IpatchItem *item, int pos) { const GType *child_types; IpatchIter iter; GType type; g_return_if_fail(IPATCH_IS_CONTAINER(container)); g_return_if_fail(IPATCH_IS_ITEM(item)); type = G_OBJECT_TYPE(item); /* get container child types */ child_types = ipatch_container_get_child_types(container); for(; *child_types; child_types++) /* loop over child types */ if(g_type_is_a(type, *child_types)) /* item type matches child type? */ { break; } if(*child_types) /* matching child type found? */ { IPATCH_ITEM_WLOCK(container); if(!ipatch_container_init_iter(container, &iter, *child_types)) { IPATCH_ITEM_WUNLOCK(container); return; } /* if position is less than 1 or off the end, get last object */ if(pos < 0 || !ipatch_iter_index(&iter, pos)) { ipatch_iter_last(&iter); } ipatch_container_insert_iter(container, item, &iter); IPATCH_ITEM_WUNLOCK(container); ipatch_container_add_notify(container, item); /* container add notify */ } else g_critical(IPATCH_CONTAINER_ERRMSG_INVALID_CHILD_2, g_type_name(type), g_type_name(G_OBJECT_TYPE(container))); } /** * ipatch_container_append: * @container: Container item * @item: Item to append * * Appends an item to a container's children. */ void ipatch_container_append(IpatchContainer *container, IpatchItem *item) { ipatch_container_insert(container, item, -1); } /** * ipatch_container_add: * @container: Container item * @item: Item to append * * Just an alias for ipatch_container_append(). */ void ipatch_container_add(IpatchContainer *container, IpatchItem *item) { ipatch_container_insert(container, item, -1); } /** * ipatch_container_prepend: * @container: Container item * @item: Item to insert * * Prepends an item to a container's children. */ void ipatch_container_prepend(IpatchContainer *container, IpatchItem *item) { ipatch_container_insert(container, item, 0); } /** * ipatch_container_remove: * @container: Container item to remove from * @item: Item to remove from @container * * Removes an @item from @container. */ void ipatch_container_remove(IpatchContainer *container, IpatchItem *item) { const GType *child_types; IpatchIter iter; GType type; GObject *obj; g_return_if_fail(IPATCH_IS_CONTAINER(container)); g_return_if_fail(IPATCH_IS_ITEM(item)); g_return_if_fail(ipatch_item_peek_parent(item) == (IpatchItem *)container); /* do container remove notify (despite the fact that the remove could be * invalid, not actually a child of container) */ ipatch_container_remove_notify(container, item); type = G_OBJECT_TYPE(item); /* get container child types */ child_types = ipatch_container_get_child_types(container); while(*child_types) /* loop over child types */ { if(g_type_is_a(type, *child_types)) /* item type matches child type? */ { IPATCH_ITEM_WLOCK(container); if(!ipatch_container_init_iter(container, &iter, *child_types)) { IPATCH_ITEM_WUNLOCK(container); return; } /* search for @item */ obj = ipatch_iter_first(&iter); while(obj && obj != (GObject *)item) { obj = ipatch_iter_next(&iter); } /* remove the object if found */ if(obj) { ipatch_container_remove_iter(container, &iter); } IPATCH_ITEM_WUNLOCK(container); if(obj) { return; /* return if removed, otherwise keep searching */ } } child_types++; } g_critical("Child of type '%s' not found in parent of type '%s'", g_type_name(type), g_type_name(G_OBJECT_TYPE(container))); } /** * ipatch_container_remove_all: * @container: Container object * * Removes all items from a @container object. */ void ipatch_container_remove_all(IpatchContainer *container) { IpatchList *list; const GType *child_types, *ptype; GList *p; g_return_if_fail(IPATCH_IS_CONTAINER(container)); child_types = ipatch_container_get_child_types(container); /* loop over child types */ for(ptype = child_types; *ptype; ptype++) { list = ipatch_container_get_children(container, *ptype); /* ++ ref new list */ for(p = list->items; p; p = p->next) { ipatch_container_remove(container, (IpatchItem *)(p->data)); } g_object_unref(list); /* -- unref list */ } } /** * ipatch_container_count: * @container: Container object to count children of * @type: Type of children to count * * Counts children of a specific @type (or derived thereof) in * a @container object. * * Returns: Count of children of @type in @container. */ guint ipatch_container_count(IpatchContainer *container, GType type) { const GType *child_types; IpatchIter iter; int count = 0; g_return_val_if_fail(IPATCH_IS_CONTAINER(container), 0); g_return_val_if_fail(g_type_is_a(type, G_TYPE_OBJECT), 0); /* get container child types */ child_types = ipatch_container_get_child_types(container); while(*child_types) /* loop over child types */ { if(g_type_is_a(*child_types, type)) /* child type matches type? */ { IPATCH_ITEM_RLOCK(container); if(!ipatch_container_init_iter(container, &iter, *child_types)) { IPATCH_ITEM_RUNLOCK(container); return 0; } count += ipatch_iter_count(&iter); IPATCH_ITEM_RUNLOCK(container); } child_types++; } return (count); } /** * ipatch_container_make_unique: * @container: Patch item container * @item: An item of a type that can be added to @container (but not * necessarily parented to @container). * * Ensures an @item's duplicate sensitive properties are unique among items of * the same type in @container. The item need not be a child of @container, but * can be. The initial values of the duplicate sensitive properties are used * if already unique, otherwise they are modified (name strings have numbers * appended to them, unused MIDI locale is found, etc). */ void ipatch_container_make_unique(IpatchContainer *container, IpatchItem *item) { IpatchContainerClass *klass; g_return_if_fail(IPATCH_IS_CONTAINER(container)); g_return_if_fail(IPATCH_IS_ITEM(item)); klass = IPATCH_CONTAINER_GET_CLASS(container); if(klass->make_unique) { (*klass->make_unique)(container, item); } } /** * ipatch_container_add_unique: * @container: Container to add item to * @item: Item to add * * Adds a patch item to a container and ensures that the item's duplicate * sensitive properties are unique (see ipatch_container_make_unique()). */ void ipatch_container_add_unique(IpatchContainer *container, IpatchItem *item) { g_return_if_fail(IPATCH_IS_CONTAINER(container)); g_return_if_fail(IPATCH_IS_ITEM(item)); IPATCH_ITEM_WLOCK(container); ipatch_container_make_unique(container, item); ipatch_container_append(container, item); IPATCH_ITEM_WUNLOCK(container); } /** * ipatch_container_init_iter: * @container: Container object * @iter: (out): Iterator structure to initialize * @type: Container child type list to initialize @iter to * * Initialize an iterator structure to a child list of the specified * @type in a @container object. * * MT-NOTE: The @container must be locked, or single thread access * ensured, for the duration of this call and iteration of @iter as * the @container object's lists are used directly. The iterator related * functions are meant to be used for latency critical operations, and they * should try and minimize the locking duration. * * Returns: %TRUE on success, %FALSE otherwise in which case the contents of * @iter are undefined. */ gboolean ipatch_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type) { IpatchContainerClass *klass; g_return_val_if_fail(IPATCH_IS_CONTAINER(container), FALSE); g_return_val_if_fail(iter != NULL, FALSE); g_return_val_if_fail(g_type_is_a(type, IPATCH_TYPE_ITEM), FALSE); klass = IPATCH_CONTAINER_GET_CLASS(container); g_return_val_if_fail(klass->init_iter != NULL, FALSE); return ((*klass->init_iter)(container, iter, type)); } /** * ipatch_container_insert_iter: * @container: Container object * @item: Patch item to insert * @iter: (nullable): Iterator marking position to insert after (%NULL position to * prepend). Iterator is advanced after insert to point to new * inserted item. Note that this makes appending multiple items rather * fast. * * This function should not normally be used. It is provided to allow for * custom high performance functions involving container adds. If used, certain * precautions and requirements must be followed. * * Insert a patch @item into a @container after the position marked by * @iter. No checking is done to see if child @item is actually a valid * type in @container, this is left up to the caller for added performance. * Also left up to the caller is a call to ipatch_container_add_notify() to * notify that the item has been added (should be called after this function * and outside of @container lock). It is not necessary to notify if hooks * are not enabled for @container or caller doesn't care. * * MT-NOTE: The @container object should be write locked, or single thread * access ensured, for the duration of this call and prior iterator functions. */ void ipatch_container_insert_iter(IpatchContainer *container, IpatchItem *item, IpatchIter *iter) { g_return_if_fail(IPATCH_IS_CONTAINER(container)); g_return_if_fail(IPATCH_IS_ITEM(item)); g_return_if_fail(iter != NULL); ipatch_iter_insert(iter, (GObject *)item); g_object_ref((GObject *)item); /* ++ ref object for container */ ipatch_item_set_parent(item, (IpatchItem *)container); /* set item parent */ } /** * ipatch_container_remove_iter: * @container: Container object * @iter: Iterator marking item to remove * * This function should not normally be used. It is provided to allow for * custom high performance functions involving container removes. If used, * certain precautions and requirements must be followed. * * Removes an item from a @container object. The item is specified by the * current position in @iter. It is left up to the caller to call * ipatch_container_remove_notify() (should be called before this function * and out of @container lock) to notify that the item will be removed. * It is not necessary to notify if hooks are not enabled for @container or * caller doesn't care. * * MT-NOTE: The @container object should be write locked, or single thread * access ensured, for the duration of this call and prior iterator functions. */ void ipatch_container_remove_iter(IpatchContainer *container, IpatchIter *iter) { GObject *obj; g_return_if_fail(IPATCH_IS_CONTAINER(container)); g_return_if_fail(iter != NULL); obj = ipatch_iter_get(iter); g_return_if_fail(obj != NULL); ipatch_iter_remove(iter); ipatch_item_unparent(IPATCH_ITEM(obj)); /* unparent item */ g_object_unref(obj); /* -- unref object for container */ } libinstpatch-1.1.6/libinstpatch/IpatchContainer.h000066400000000000000000000161171400263525300221210ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_CONTAINER_H__ #define __IPATCH_CONTAINER_H__ #include #include #include /* forward type declarations */ typedef struct _IpatchContainer IpatchContainer; typedef struct _IpatchContainerClass IpatchContainerClass; #include #include #include #define IPATCH_TYPE_CONTAINER (ipatch_container_get_type ()) #define IPATCH_CONTAINER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_CONTAINER, IpatchContainer)) #define IPATCH_CONTAINER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_CONTAINER, \ IpatchContainerClass)) #define IPATCH_IS_CONTAINER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_CONTAINER)) #define IPATCH_IS_CONTAINER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_CONTAINER)) #define IPATCH_CONTAINER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_CONTAINER, \ IpatchContainerClass)) /** * IpatchContainerCallback: * @container: Container item * @item: Item that was added/removed to/from @container * @user_data: User defined pointer assigned when callback connected * * A function prototype callback which is called for container adds or removes * (after adds, before removes). */ typedef void (*IpatchContainerCallback)(IpatchContainer *container, IpatchItem *item, gpointer user_data); /** * IpatchContainerDisconnect: * @container: Container item * @child: Match child item of original connect * (ipatch_container_remove_connect() only, always NULL for * ipatch_container_add_connect()). * @user_data: User defined pointer assigned when callback connected * * A function prototype which is called when a callback gets disconnected. */ typedef void (*IpatchContainerDisconnect)(IpatchContainer *container, IpatchItem *child, gpointer user_data); /* Base patch container object */ struct _IpatchContainer { IpatchItem parent_instance; }; struct _IpatchContainerClass { IpatchItemClass parent_class; /*< public >*/ /* methods */ const GType *(*child_types)(void); const GType *(*virtual_types)(void); gboolean(*init_iter)(IpatchContainer *container, IpatchIter *iter, GType type); void (*make_unique)(IpatchContainer *container, IpatchItem *item); gboolean(*get_dups)(IpatchContainer *container, IpatchItem *item, IpatchList **list); }; /* container uses no item flags */ /** * IPATCH_CONTAINER_UNUSED_FLAG_SHIFT: (skip) */ #define IPATCH_CONTAINER_UNUSED_FLAG_SHIFT IPATCH_ITEM_UNUSED_FLAG_SHIFT /** * IPATCH_CONTAINER_ERRMSG_INVALID_CHILD_2: (skip) */ #define IPATCH_CONTAINER_ERRMSG_INVALID_CHILD_2 \ "Invalid child type '%s' for parent type '%s'" GType ipatch_container_get_type(void); IpatchList *ipatch_container_get_children(IpatchContainer *container, GType type); GList *ipatch_container_get_children_list(IpatchContainer *container); GList *ipatch_container_get_children_by_type(IpatchContainer *container, GType type); const GType *ipatch_container_get_child_types(IpatchContainer *container); const GType *ipatch_container_get_virtual_types(IpatchContainer *container); const GType *ipatch_container_type_get_child_types(GType container_type); void ipatch_container_insert(IpatchContainer *container, IpatchItem *item, int pos); void ipatch_container_append(IpatchContainer *container, IpatchItem *item); void ipatch_container_add(IpatchContainer *container, IpatchItem *item); void ipatch_container_prepend(IpatchContainer *container, IpatchItem *item); void ipatch_container_remove(IpatchContainer *container, IpatchItem *item); void ipatch_container_remove_all(IpatchContainer *container); guint ipatch_container_count(IpatchContainer *container, GType type); void ipatch_container_make_unique(IpatchContainer *container, IpatchItem *item); void ipatch_container_add_unique(IpatchContainer *container, IpatchItem *item); gboolean ipatch_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type); void ipatch_container_insert_iter(IpatchContainer *container, IpatchItem *item, IpatchIter *iter); void ipatch_container_remove_iter(IpatchContainer *container, IpatchIter *iter); /* defined in IpatchContainer_notify.c */ void ipatch_container_add_notify(IpatchContainer *container, IpatchItem *child); void ipatch_container_remove_notify(IpatchContainer *container, IpatchItem *child); guint ipatch_container_add_connect(IpatchContainer *container, IpatchContainerCallback callback, IpatchContainerDisconnect disconnect, gpointer user_data); guint ipatch_container_add_connect_notify(IpatchContainer *container, IpatchContainerCallback callback, GDestroyNotify notify_func, gpointer user_data); guint ipatch_container_remove_connect(IpatchContainer *container, IpatchItem *child, IpatchContainerCallback callback, IpatchContainerDisconnect disconnect, gpointer user_data); guint ipatch_container_remove_connect_notify(IpatchContainer *container, IpatchItem *child, IpatchContainerCallback callback, GDestroyNotify notify_func, gpointer user_data); void ipatch_container_add_disconnect(guint handler_id); void ipatch_container_add_disconnect_matched(IpatchContainer *container, IpatchContainerCallback callback, gpointer user_data); void ipatch_container_remove_disconnect(guint handler_id); void ipatch_container_remove_disconnect_matched(IpatchContainer *container, IpatchItem *child, IpatchContainerCallback callback, gpointer user_data); #endif libinstpatch-1.1.6/libinstpatch/IpatchContainer_notify.c000066400000000000000000001024631400263525300235040ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /* * IpatchContainer_notify.c - Container add/remove callback notify system */ #include #include "IpatchContainer.h" #include "ipatch_priv.h" /* hash value used for IpatchContainer callbacks */ typedef struct { IpatchContainerCallback callback; /* callback function */ IpatchContainerDisconnect disconnect; /* called when callback is disconnected */ GDestroyNotify notify_func; /* destroy notify function (this or disconnect will be set but not both) */ gpointer user_data; /* user data to pass to function */ guint handler_id; /* unique handler ID */ } ContainerCallback; static void _ipatch_container_free_container_callback(ContainerCallback *cb); static void _ipatch_container_free_gslist(GSList *list); static gboolean _ipatch_container_callback_hash_free_value(gpointer key, gpointer value, gpointer user_data); static guint ipatch_container_real_add_connect(IpatchContainer *container, IpatchContainerCallback callback, IpatchContainerDisconnect disconnect, GDestroyNotify notify_func, gpointer user_data); static guint ipatch_container_real_remove_connect(IpatchContainer *container, IpatchItem *child, IpatchContainerCallback callback, IpatchContainerDisconnect disconnect, GDestroyNotify notify_func, gpointer user_data); static void ipatch_container_real_disconnect(guint handler_id, IpatchContainer *container, IpatchItem *child, IpatchContainerCallback callback, gpointer user_data, gboolean isadd); static gboolean callback_hash_GHRFunc(gpointer key, gpointer value, gpointer user_data); /* lock for add_callback_next_id, add_callback_hash, and add_wild_callback_list */ G_LOCK_DEFINE_STATIC(add_callbacks); static guint add_callback_next_id = 1; /* next container add handler ID */ /* hash of container add callbacks (IpatchContainer -> GSList) */ static GHashTable *add_callback_hash; static GSList *add_wild_callback_list = NULL; /* container add wildcard cbs */ /* lock for remove_callback_next_id, remove_callback_hash and remove_wild_callback_list */ G_LOCK_DEFINE_STATIC(remove_callbacks); static guint remove_callback_next_id = 1; /* next container remove handler ID */ /* container remove callbacks */ static GHashTable *remove_container_callback_hash; /* IpatchContainer -> GSList */ static GHashTable *remove_child_callback_hash; /* IpatchItem -> GSList */ static GSList *remove_wild_callback_list = NULL; /* container add wildcard cbs */ /* - Initialization/deinitialization of 'container add/remove notify system' */ /** * _ipatch_container_notify_init: (skip) */ void _ipatch_container_notify_init (void) { /* one time hash init for container callbacks */ /* callback pool on action: add to container */ add_callback_next_id = 1; add_callback_hash = g_hash_table_new (NULL, NULL); add_wild_callback_list = NULL; /* callback pool on action: remove out of container */ remove_callback_next_id = 1; remove_container_callback_hash = g_hash_table_new (NULL, NULL); remove_child_callback_hash = g_hash_table_new (NULL, NULL); remove_wild_callback_list = NULL; } /* free calback pool of 'container add/remove notify system' */ void _ipatch_container_notify_deinit(void) { /* free calback pool of 'container add notify system' */ g_hash_table_foreach_remove(add_callback_hash, _ipatch_container_callback_hash_free_value, NULL); g_hash_table_destroy(add_callback_hash); _ipatch_container_free_gslist(add_wild_callback_list); /* free calback pool of 'container remove notify system' */ g_hash_table_foreach_remove(remove_container_callback_hash, _ipatch_container_callback_hash_free_value, NULL); g_hash_table_destroy(remove_container_callback_hash); g_hash_table_foreach_remove(remove_child_callback_hash, _ipatch_container_callback_hash_free_value, NULL); g_hash_table_destroy(remove_child_callback_hash); _ipatch_container_free_gslist(remove_wild_callback_list); } /* free hash value entry */ static gboolean _ipatch_container_callback_hash_free_value(gpointer key, gpointer value, gpointer user_data) { _ipatch_container_free_gslist((GSList *)value); return TRUE; } /* free GSList */ static void _ipatch_container_free_gslist(GSList *list) { g_slist_free_full(list, (GDestroyNotify)_ipatch_container_free_container_callback); } static void _ipatch_container_free_container_callback(ContainerCallback *cb) { g_slice_free(ContainerCallback, cb); } /* ----------- API of 'container add/remove notify system' ------------------*/ /** * ipatch_container_add_notify: * @container: Container item for which an item add occurred * @child: Child which was added * * Notify that a child add has occurred to an #IpatchContainer object. * Should be called after the add has occurred. This function is normally * not needed except when using ipatch_container_insert_iter(). */ void ipatch_container_add_notify(IpatchContainer *container, IpatchItem *child) { /* dynamically adjustable max callbacks to allocate cbarray for */ static guint max_callbacks = 64; ContainerCallback *cbarray; /* stack allocated callback array */ ContainerCallback *cb; ContainerCallback *old_cbarray; guint old_max_callbacks; GSList *match_container, *wild_list; guint cbindex = 0, i; g_return_if_fail(IPATCH_IS_CONTAINER(container)); g_return_if_fail(IPATCH_IS_ITEM(child)); /* Container has changed */ ipatch_item_changed((IpatchItem *)container); /* if hooks not active for container, just return */ if(!(ipatch_item_get_flags(container) & IPATCH_ITEM_HOOKS_ACTIVE)) { return; } /* allocate callback array on stack (for performance) */ cbarray = g_alloca(max_callbacks * sizeof(ContainerCallback)); G_LOCK(add_callbacks); /* lookup callback list for this container */ match_container = g_hash_table_lookup(add_callback_hash, container); wild_list = add_wild_callback_list; /* duplicate lists into array (since we will call them outside of lock) */ for(; match_container && cbindex < max_callbacks; match_container = match_container->next, cbindex++) { cb = (ContainerCallback *)(match_container->data); cbarray[cbindex].callback = cb->callback; cbarray[cbindex].user_data = cb->user_data; } for(; wild_list && cbindex < max_callbacks; wild_list = wild_list->next, cbindex++) { cb = (ContainerCallback *)(wild_list->data); cbarray[cbindex].callback = cb->callback; cbarray[cbindex].user_data = cb->user_data; } if(match_container || wild_list) { /* We exceeded max_callbacks (Bender just shit a brick) */ old_cbarray = cbarray; old_max_callbacks = max_callbacks; cbindex += g_slist_length(match_container); cbindex += g_slist_length(wild_list); max_callbacks = cbindex + 16; /* plus some for kicks */ cbarray = g_alloca(max_callbacks * sizeof(ContainerCallback)); memcpy(cbarray, old_cbarray, old_max_callbacks * sizeof(ContainerCallback)); cbindex = old_max_callbacks; /* duplicate rest of the lists */ for(; match_container && cbindex < max_callbacks; match_container = match_container->next, cbindex++) { cb = (ContainerCallback *)(match_container->data); cbarray[cbindex].callback = cb->callback; cbarray[cbindex].user_data = cb->user_data; } for(; wild_list && cbindex < max_callbacks; wild_list = wild_list->next, cbindex++) { cb = (ContainerCallback *)(wild_list->data); cbarray[cbindex].callback = cb->callback; cbarray[cbindex].user_data = cb->user_data; } } G_UNLOCK(add_callbacks); /* call the callbacks */ for(i = 0; i < cbindex; i++) { cb = &cbarray[i]; cb->callback(container, child, cb->user_data); } } /** * ipatch_container_remove_notify: * @container: Container item for which a remove will occur * @child: Child which will be removed * * Notify that a container remove will occur to an #IpatchContainer object. * Should be called before the remove occurs. This function is normally not * needed, except when using ipatch_container_remove_iter(). */ void ipatch_container_remove_notify(IpatchContainer *container, IpatchItem *child) { /* dynamically adjustable max callbacks to allocate cbarray for */ static guint max_callbacks = 64; ContainerCallback *cbarray; /* stack allocated callback array */ ContainerCallback *cb; ContainerCallback *old_cbarray; guint old_max_callbacks; GSList *match_container, *match_child, *wild_list; guint cbindex = 0, i; g_return_if_fail(IPATCH_IS_CONTAINER(container)); g_return_if_fail(IPATCH_IS_ITEM(child)); /* Container has changed */ ipatch_item_changed((IpatchItem *)container); /* if hooks not active for container, just return */ if(!(ipatch_item_get_flags(container) & IPATCH_ITEM_HOOKS_ACTIVE)) { return; } /* allocate callback array on stack (for performance) */ cbarray = g_alloca(max_callbacks * sizeof(ContainerCallback)); G_LOCK(remove_callbacks); /* lookup callback list for container */ match_container = g_hash_table_lookup(remove_container_callback_hash, container); /* lookup callback list for child */ match_child = g_hash_table_lookup(remove_child_callback_hash, child); wild_list = remove_wild_callback_list; /* duplicate lists into array (since we will call them outside of lock) */ for(; match_container && cbindex < max_callbacks; match_container = match_container->next, cbindex++) { cb = (ContainerCallback *)(match_container->data); cbarray[cbindex].callback = cb->callback; cbarray[cbindex].user_data = cb->user_data; } for(; match_child && cbindex < max_callbacks; match_child = match_child->next, cbindex++) { cb = (ContainerCallback *)(match_child->data); cbarray[cbindex].callback = cb->callback; cbarray[cbindex].user_data = cb->user_data; } for(; wild_list && cbindex < max_callbacks; wild_list = wild_list->next, cbindex++) { cb = (ContainerCallback *)(wild_list->data); cbarray[cbindex].callback = cb->callback; cbarray[cbindex].user_data = cb->user_data; } if(match_container || match_child || wild_list) { /* We exceeded max_callbacks (Bender just shit a brick) */ old_cbarray = cbarray; old_max_callbacks = max_callbacks; cbindex += g_slist_length(match_container); cbindex += g_slist_length(match_child); cbindex += g_slist_length(wild_list); max_callbacks = cbindex + 16; /* plus some for kicks */ cbarray = g_alloca(max_callbacks * sizeof(ContainerCallback)); memcpy(cbarray, old_cbarray, old_max_callbacks * sizeof(ContainerCallback)); cbindex = old_max_callbacks; /* duplicate rest of the lists */ for(; match_container && cbindex < max_callbacks; match_container = match_container->next, cbindex++) { cb = (ContainerCallback *)(match_container->data); cbarray[cbindex].callback = cb->callback; cbarray[cbindex].user_data = cb->user_data; } for(; match_child && cbindex < max_callbacks; match_child = match_child->next, cbindex++) { cb = (ContainerCallback *)(match_child->data); cbarray[cbindex].callback = cb->callback; cbarray[cbindex].user_data = cb->user_data; } for(; wild_list && cbindex < max_callbacks; wild_list = wild_list->next, cbindex++) { cb = (ContainerCallback *)(wild_list->data); cbarray[cbindex].callback = cb->callback; cbarray[cbindex].user_data = cb->user_data; } } G_UNLOCK(remove_callbacks); /* call the callbacks */ for(i = 0; i < cbindex; i++) { cb = &cbarray[i]; cb->callback(container, child, cb->user_data); } } /** * ipatch_container_add_connect: (skip) * @container: (nullable): Container to match (%NULL for wildcard) * @callback: Callback function to call on match * @disconnect: (nullable): Function to call when callback is disconnected or %NULL * @user_data: User defined data pointer to pass to @callback and @disconnect * * Adds a callback which gets called when a container item add operation occurs * and the container matches @container. When @container is %NULL, @callback * will be called for every container add operation. * * Returns: Handler ID which can be used to disconnect the callback or * 0 on error (only occurs on invalid function parameters). */ guint ipatch_container_add_connect(IpatchContainer *container, IpatchContainerCallback callback, IpatchContainerDisconnect disconnect, gpointer user_data) { return (ipatch_container_real_add_connect(container, callback, disconnect, NULL, user_data)); } /** * ipatch_container_add_connect_notify: (rename-to ipatch_container_add_connect) * @container: (nullable): Container to match (%NULL for wildcard) * @callback: (scope notified) (closure user_data): Callback function to call on match * @notify_func: (scope async) (closure user_data) (nullable): Callback destroy notify * when callback is disconnected or %NULL * @user_data: (nullable): User defined data pointer to pass to @callback and @disconnect * * Adds a callback which gets called when a container item add operation occurs * and the container matches @container. When @container is %NULL, @callback * will be called for every container add operation. * * Returns: Handler ID which can be used to disconnect the callback or * 0 on error (only occurs on invalid function parameters). * * Since: 1.1.0 */ guint ipatch_container_add_connect_notify(IpatchContainer *container, IpatchContainerCallback callback, GDestroyNotify notify_func, gpointer user_data) { return (ipatch_container_real_add_connect(container, callback, NULL, notify_func, user_data)); } static guint ipatch_container_real_add_connect(IpatchContainer *container, IpatchContainerCallback callback, IpatchContainerDisconnect disconnect, GDestroyNotify notify_func, gpointer user_data) { ContainerCallback *cb; GSList *cblist; guint handler_id; g_return_val_if_fail(!container || IPATCH_IS_CONTAINER(container), 0); g_return_val_if_fail(callback != NULL, 0); cb = g_slice_new(ContainerCallback); cb->callback = callback; cb->disconnect = disconnect; cb->user_data = user_data; G_LOCK(add_callbacks); handler_id = add_callback_next_id++; cb->handler_id = handler_id; if(container) { /* get existing list for Container (if any) */ cblist = g_hash_table_lookup(add_callback_hash, container); /* update the list in the hash table */ g_hash_table_insert(add_callback_hash, container, g_slist_prepend(cblist, cb)); } else /* callback is wildcard, just add to the wildcard list */ { add_wild_callback_list = g_slist_prepend(add_wild_callback_list, cb); } G_UNLOCK(add_callbacks); return (handler_id); } /** * ipatch_container_remove_connect: (skip) * @container: (nullable): Container to match (%NULL for wildcard) * @child: (nullable): Child item to match (%NULL for wildcard) * @callback: Callback function to call on match * @disconnect: (nullable): Function to call when callback is disconnected or %NULL * @user_data: (closure): User defined data pointer to pass to @callback and @disconnect * * Adds a callback which gets called when a container item remove operation * occurs and the container matches @container and child item matches @child. * The @container and/or @child parameters can be %NULL in which case they are * wildcard. If both are %NULL then @callback will be called for every * container remove operation. Note that specifying only @child or both * @container and @child is the same, since a child belongs to only one container. * * Returns: Handler ID which can be used to disconnect the callback or * 0 on error (only occurs on invalid function parameters). */ guint ipatch_container_remove_connect(IpatchContainer *container, IpatchItem *child, IpatchContainerCallback callback, IpatchContainerDisconnect disconnect, gpointer user_data) { return (ipatch_container_real_remove_connect(container, child, callback, disconnect, NULL, user_data)); } /** * ipatch_container_remove_connect_notify: (rename-to ipatch_container_remove_connect) * @container: (nullable): Container to match (%NULL for wildcard) * @child: (nullable): Child item to match (%NULL for wildcard) * @callback: (scope notified) (closure user_data): Callback function to call on match * @notify_func: (scope async) (closure user_data) (nullable): Function to call * when callback is disconnected or %NULL * @user_data: (nullable): User defined data pointer to pass to @callback and @disconnect * * Adds a callback which gets called when a container item remove operation * occurs and the container matches @container and child item matches @child. * The @container and/or @child parameters can be %NULL in which case they are * wildcard. If both are %NULL then @callback will be called for every * container remove operation. Note that specifying only @child or both * @container and @child is the same, since a child belongs to only one container. * * Returns: Handler ID which can be used to disconnect the callback or * 0 on error (only occurs on invalid function parameters). * * Since: 1.1.0 */ guint ipatch_container_remove_connect_notify(IpatchContainer *container, IpatchItem *child, IpatchContainerCallback callback, GDestroyNotify notify_func, gpointer user_data) { return (ipatch_container_real_remove_connect(container, child, callback, NULL, notify_func, user_data)); } static guint ipatch_container_real_remove_connect(IpatchContainer *container, IpatchItem *child, IpatchContainerCallback callback, IpatchContainerDisconnect disconnect, GDestroyNotify notify_func, gpointer user_data) { ContainerCallback *cb; GSList *cblist; guint handler_id; g_return_val_if_fail(!container || IPATCH_IS_CONTAINER(container), 0); g_return_val_if_fail(!child || IPATCH_IS_ITEM(child), 0); g_return_val_if_fail(callback != NULL, 0); cb = g_slice_new(ContainerCallback); cb->callback = callback; cb->disconnect = disconnect; cb->notify_func = notify_func; cb->user_data = user_data; G_LOCK(remove_callbacks); handler_id = remove_callback_next_id++; cb->handler_id = handler_id; if(child) /* child and container:child are equivalent (child has only 1 parent) */ { /* get existing list for child (if any) */ cblist = g_hash_table_lookup(remove_child_callback_hash, child); /* update new list head */ g_hash_table_insert(remove_child_callback_hash, child, g_slist_prepend(cblist, cb)); } else if(container) { /* get existing list for container (if any) */ cblist = g_hash_table_lookup(remove_container_callback_hash, container); /* update new list head */ g_hash_table_insert(remove_container_callback_hash, container, g_slist_prepend(cblist, cb)); } else /* callback is completely wildcard, just add to the wildcard list */ { remove_wild_callback_list = g_slist_prepend(remove_wild_callback_list, cb); } G_UNLOCK(remove_callbacks); return (handler_id); } /** * ipatch_container_add_disconnect: * @handler_id: ID of callback handler * * Disconnects a container add callback previously connected with * ipatch_container_add_connect() by handler ID. The * ipatch_container_add_disconnect_matched() function can be used instead to * disconnect by original callback criteria and is actually faster. */ void ipatch_container_add_disconnect(guint handler_id) { g_return_if_fail(handler_id != 0); ipatch_container_real_disconnect(handler_id, NULL, NULL, NULL, NULL, TRUE); } /** * ipatch_container_add_disconnect_matched: (skip) * @container: Container to match * @callback: Callback function to match * @user_data: User data to match * * Disconnects a container add callback previously connected with * ipatch_container_add_connect() by match criteria. */ void ipatch_container_add_disconnect_matched(IpatchContainer *container, IpatchContainerCallback callback, gpointer user_data) { g_return_if_fail(callback != NULL); ipatch_container_real_disconnect(0, container, NULL, callback, user_data, TRUE); } /** * ipatch_container_remove_disconnect: * @handler_id: ID of callback handler * * Disconnects a container remove callback previously connected with * ipatch_container_remove_connect() by handler ID. The * ipatch_container_remove_disconnect_matched() function can be used instead to * disconnect by original callback criteria and is actually faster. */ void ipatch_container_remove_disconnect(guint handler_id) { g_return_if_fail(handler_id != 0); ipatch_container_real_disconnect(handler_id, NULL, NULL, NULL, NULL, FALSE); } /** * ipatch_container_remove_disconnect_matched: (skip) * @container: (nullable): Container to match (can be %NULL if @child is set) * @child: (nullable): Child item to match (can be %NULL if @container is set) * @callback: Callback function to match * @user_data: User data to match * * Disconnects a handler previously connected with * ipatch_container_remove_connect() by match criteria. */ void ipatch_container_remove_disconnect_matched(IpatchContainer *container, IpatchItem *child, IpatchContainerCallback callback, gpointer user_data) { g_return_if_fail(callback != NULL); ipatch_container_real_disconnect(0, container, child, callback, user_data, FALSE); } /* a bag used in ipatch_container_real_disconnect */ typedef struct { gboolean found; /* Set to TRUE if handler found */ IpatchItem *match; /* container or child - in: (match only), out */ ContainerCallback cb; /* in: handler_id, out: disconnect, user_data */ gpointer update_key; /* out: key of list root requiring update */ GSList *update_list; /* out: new root of list to update */ gboolean update_needed; /* out: set when a list root needs updating */ gboolean isadd; /* same value as function parameter */ } DisconnectBag; /* function for removing a callback using match criteria (hash table lookup). * Faster than iterating over the hash (required when searching by ID). */ static void disconnect_matched(GHashTable *hash, DisconnectBag *bag) { ContainerCallback *cb; GSList *list, *newroot, *p; list = g_hash_table_lookup(hash, bag->match); if(!list) { return; } for(p = list; p; p = p->next) /* loop over callbacks in list */ { cb = (ContainerCallback *)(p->data); /* matches criteria? */ if(cb->callback == bag->cb.callback && cb->user_data == bag->cb.user_data) { /* return callback's disconnect func */ bag->found = TRUE; bag->cb.disconnect = cb->disconnect; g_slice_free(ContainerCallback, cb); newroot = g_slist_delete_link(list, p); if(!newroot) /* no more list? - remove hash entry */ { g_hash_table_remove(hash, bag->match); } else if(newroot != list) /* root of list changed? - update hash entry */ { g_hash_table_insert(hash, bag->match, newroot); } break; } } } /* Used by disconnect functions. * Either handler_id should be set to a non-zero value or the other * parameters should be assigned but not both. * isadd specifies if the handler is an add callback (TRUE) or remove * callback (FALSE). */ static void ipatch_container_real_disconnect(guint handler_id, IpatchContainer *container, IpatchItem *child, IpatchContainerCallback callback, gpointer user_data, gboolean isadd) { ContainerCallback *cb; DisconnectBag bag = { 0 }; gboolean isfoundchild = FALSE; GSList *p; g_return_if_fail(handler_id != 0 || callback != 0); g_return_if_fail(handler_id == 0 || callback == 0); if(!handler_id) /* find by match criteria? */ { bag.match = child ? child : (IpatchItem *)container; bag.cb.callback = callback; bag.cb.user_data = user_data; } else { bag.cb.handler_id = handler_id; /* find by handler ID */ } bag.isadd = isadd; if(isadd) /* add callback search? */ { G_LOCK(add_callbacks); if(handler_id) /* search by ID? */ { /* scan every list in add callback hash and remove if found */ g_hash_table_foreach_remove(add_callback_hash, callback_hash_GHRFunc, &bag); if(bag.update_needed) /* update root of list if needed (can't do that in GHRFunc) */ { g_hash_table_insert(add_callback_hash, bag.update_key, bag.update_list); } } else { disconnect_matched(add_callback_hash, &bag); /* lookup by match and remove if found */ } /* if not found, check wildcard list (if search by handler ID * or NULL container) */ if(!bag.found && (handler_id || !container)) { for(p = add_wild_callback_list; p; p = p->next) { cb = (ContainerCallback *)(p->data); if((handler_id && cb->handler_id == handler_id) || (!handler_id && cb->callback == callback && cb->user_data == user_data)) { bag.found = TRUE; bag.cb.disconnect = cb->disconnect; bag.cb.user_data = cb->user_data; g_slice_free(ContainerCallback, cb); add_wild_callback_list = g_slist_delete_link(add_wild_callback_list, p); break; } } } G_UNLOCK(add_callbacks); } else /* remove callback search */ { G_LOCK(remove_callbacks); /* check child remove callback list if search by ID or child is set */ if(handler_id) /* search by ID? */ { g_hash_table_foreach_remove(remove_child_callback_hash, callback_hash_GHRFunc, &bag); if(bag.update_needed) /* update root of list if needed (can't do that in GHRFunc) */ g_hash_table_insert(remove_child_callback_hash, bag.update_key, bag.update_list); } else if(child) /* match by child? */ { disconnect_matched(remove_child_callback_hash, &bag); } if(bag.found) { isfoundchild = TRUE; /* indicate it is a child callback */ } if(!bag.found) /* not yet found? */ { /* check container remove callback list if search by ID or container is set */ if(handler_id) /* search by ID? */ { g_hash_table_foreach_remove(remove_container_callback_hash, callback_hash_GHRFunc, &bag); if(bag.update_needed) /* update root of list if needed (can't do that in GHRFunc) */ g_hash_table_insert(remove_container_callback_hash, bag.update_key, bag.update_list); } else if(container) /* match by container? */ { disconnect_matched(remove_container_callback_hash, &bag); } } /* if not yet found, check wildcard list (if search by handler ID * or NULL container and child) */ if(!bag.found && (handler_id || (!container && !child))) { for(p = remove_wild_callback_list; p; p = p->next) { cb = (ContainerCallback *)(p->data); if((handler_id && cb->handler_id == handler_id) || (!handler_id && cb->callback == callback && cb->user_data == user_data)) { bag.found = TRUE; bag.cb.disconnect = cb->disconnect; bag.cb.user_data = cb->user_data; g_slice_free(ContainerCallback, cb); remove_wild_callback_list = g_slist_delete_link(remove_wild_callback_list, p); break; } } } G_UNLOCK(remove_callbacks); } if(!bag.found) { if(handler_id) g_critical(G_STRLOC ": Failed to find %s container handler with ID '%d'", isadd ? "add" : "remove", handler_id); else g_critical(G_STRLOC ": Failed to find %s container handler with criteria %p:%p:%p:%p", isadd ? "add" : "remove", container, child, callback, user_data); } /* see if callback found and it had a disconnect func */ if(bag.cb.disconnect) { if(isfoundchild) { bag.cb.disconnect(NULL, bag.match, bag.cb.user_data); } else { bag.cb.disconnect((IpatchContainer *)bag.match, NULL, bag.cb.user_data); } } } /* finds a container add or remove handler by ID and removes it */ static gboolean callback_hash_GHRFunc(gpointer key, gpointer value, gpointer user_data) { DisconnectBag *bag = (DisconnectBag *)user_data; GSList *cblist = (GSList *)value, *p, *newroot; ContainerCallback *cb; p = cblist; while(p) /* loop over callbacks in callback list */ { cb = (ContainerCallback *)(p->data); /* matches criteria? (by ID or by match) */ if((bag->cb.handler_id && cb->handler_id == bag->cb.handler_id) || (!bag->cb.handler_id && key == bag->match && cb->callback == bag->cb.callback && cb->user_data == bag->cb.user_data)) { /* return callback's item, pspec, disconnect func and user_data */ bag->found = TRUE; bag->cb.disconnect = cb->disconnect; bag->cb.user_data = cb->user_data; bag->match = key; g_slice_free(ContainerCallback, cb); newroot = g_slist_delete_link(cblist, p); if(!newroot) { return (TRUE); /* no more list? Remove hash entry */ } /* if root not the same, return update information (can't be done in GHRFunc) */ if(newroot != cblist) { bag->update_key = key; bag->update_list = newroot; } return (FALSE); /* don't remove entry (callback list not empty) */ } p = g_slist_next(p); } return (FALSE); /* don't remove entry (item not found) */ } libinstpatch-1.1.6/libinstpatch/IpatchConvert_DLS2.c000066400000000000000000000222341400263525300223730ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchConvert_DLS2 * @short_description: DLS conversion types * @see_also: #IpatchConverter * @stability: Stable * * DLS object converter types. */ #include #include #include #include #include "misc.h" #include "IpatchConvert_DLS2.h" #include "IpatchConverter.h" #include "IpatchConverter_priv.h" #include "IpatchDLSFile.h" #include "IpatchDLS2.h" #include "IpatchDLSReader.h" #include "IpatchDLSWriter.h" #include "IpatchSampleData.h" #include "IpatchSampleStoreSndFile.h" #include "IpatchSample.h" #include "IpatchBase.h" #include "IpatchFile.h" #include "IpatchSndFile.h" #include "IpatchDLSWriter.h" #include "i18n.h" enum { PROP_0, PROP_DLS2_TO_FILE_CREATE_STORES }; static void _dls2_to_file_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void _dls2_to_file_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); /** * _ipatch_convert_DLS2_init: (skip) * * Init routine for DLS conversion types */ void _ipatch_convert_DLS2_init(void) { g_type_class_ref(IPATCH_TYPE_CONVERTER_DLS2_TO_FILE); g_type_class_ref(IPATCH_TYPE_CONVERTER_FILE_TO_DLS2); g_type_class_ref(IPATCH_TYPE_CONVERTER_FILE_TO_DLS2_SAMPLE); ipatch_register_converter_map(IPATCH_TYPE_CONVERTER_DLS2_TO_FILE, 0, 0, IPATCH_TYPE_DLS2, 0, 1, IPATCH_TYPE_DLS_FILE, IPATCH_TYPE_FILE, 1); ipatch_register_converter_map(IPATCH_TYPE_CONVERTER_FILE_TO_DLS2, 0, 0, IPATCH_TYPE_DLS_FILE, 0, 1, IPATCH_TYPE_DLS2, IPATCH_TYPE_BASE, 0); ipatch_register_converter_map(IPATCH_TYPE_CONVERTER_FILE_TO_DLS2_SAMPLE, 0, 0, IPATCH_TYPE_SND_FILE, 0, 1, IPATCH_TYPE_DLS2_SAMPLE, 0, 1); } static gboolean _dls2_to_file_convert(IpatchConverter *converter, GError **err) { IpatchDLS2 *dls; IpatchFile *file; IpatchFileHandle *handle; IpatchDLSWriter *writer; gboolean create_stores; IpatchList *stores; int retval; dls = IPATCH_DLS2(IPATCH_CONVERTER_INPUT(converter)); file = IPATCH_FILE(IPATCH_CONVERTER_OUTPUT(converter)); handle = ipatch_file_open(IPATCH_FILE(file), NULL, "w", err); if(!handle) { return (FALSE); } writer = ipatch_dls_writer_new(handle, dls); /* ++ ref new writer */ retval = ipatch_dls_writer_save(writer, err); g_object_get(converter, "create-stores", &create_stores, NULL); if(retval && create_stores) { stores = ipatch_dls_writer_create_stores(writer); // ++ reference sample stores if(stores) { ipatch_converter_add_output(converter, G_OBJECT(stores)); g_object_unref(stores); // -- unref sample stores } } g_object_unref(writer); /* -- unref writer */ return (retval); } static gboolean _file_to_dls2_convert(IpatchConverter *converter, GError **err) { IpatchDLS2 *dls; IpatchDLSFile *file; IpatchFileHandle *handle; IpatchDLSReader *reader; file = IPATCH_DLS_FILE(IPATCH_CONVERTER_INPUT(converter)); handle = ipatch_file_open(IPATCH_FILE(file), NULL, "r", err); if(!handle) { return (FALSE); } reader = ipatch_dls_reader_new(handle); /* ++ ref new reader */ dls = ipatch_dls_reader_load(reader, err); /* ++ ref loaded DLS object */ g_object_unref(reader); /* -- unref reader */ if(dls) { ipatch_converter_add_output(converter, G_OBJECT(dls)); g_object_unref(dls); /* -- unref loaded DLS object */ return (TRUE); } else { return (FALSE); } } /** * _file_to_dls2_sample_convert: (skip) * * Also used by IpatchConvert_Gig.c */ gboolean _file_to_dls2_sample_convert(IpatchConverter *converter, GError **err) { IpatchSndFile *file; IpatchDLS2Sample *dls2sample; IpatchSampleStoreSndFile *store; IpatchSampleData *sampledata; int format, rate, loop_type, root_note, fine_tune; guint length, loop_start, loop_end; char *filename, *title; file = IPATCH_SND_FILE(IPATCH_CONVERTER_INPUT(converter)); dls2sample = IPATCH_DLS2_SAMPLE(IPATCH_CONVERTER_OUTPUT(converter)); filename = ipatch_file_get_name(IPATCH_FILE(file)); /* ++ alloc file name */ if(!filename) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_PROGRAM, _("Sample file object must have a file name")); return (FALSE); } store = IPATCH_SAMPLE_STORE_SND_FILE(ipatch_sample_store_snd_file_new(filename)); /* ++ ref store */ if(!ipatch_sample_store_snd_file_init_read(store)) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNSUPPORTED, _("Sample file '%s' is invalid or unsupported"), filename); g_object_unref(store); /* -- unref store */ g_free(filename); /* -- free filename */ return (FALSE); } g_free(filename); /* -- free filename */ g_object_get(store, "title", &title, /* ++ alloc title */ "sample-size", &length, "sample-format", &format, "sample-rate", &rate, "loop-type", &loop_type, "loop-start", &loop_start, "loop-end", &loop_end, "root-note", &root_note, "fine-tune", &fine_tune, NULL); if(length < 4) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID, _("Sample '%s' is too small"), title ? title : _("")); g_free(title); /* -- free title */ g_object_unref(store); /* -- unref store */ return (FALSE); } sampledata = ipatch_sample_data_new(); /* ++ ref sampledata */ ipatch_sample_data_add(sampledata, IPATCH_SAMPLE_STORE(store)); g_object_unref(store); /* -- unref store */ g_object_set(dls2sample, "name", title, "sample-data", sampledata, "sample-rate", rate, "root-note", (root_note != -1) ? root_note : 60, "fine-tune", fine_tune, "loop-start", loop_start, "loop-end", loop_end, NULL); g_object_unref(sampledata); /* -- unref sampledata */ g_free(title); /* -- free title */ return (TRUE); } /* DLS2 -> File class is handled manually to install properties */ static void _dls2_to_file_class_init(IpatchConverterClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->get_property = _dls2_to_file_get_property; obj_class->set_property = _dls2_to_file_set_property; klass->verify = NULL; klass->notes = NULL; klass->convert = _dls2_to_file_convert; g_object_class_install_property(obj_class, PROP_DLS2_TO_FILE_CREATE_STORES, g_param_spec_boolean("create-stores", "Create stores", "Create sample stores", FALSE, G_PARAM_READWRITE)); } static void _dls2_to_file_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchConverterDLS2ToFile *converter = (IpatchConverterDLS2ToFile *)object; switch(property_id) { case PROP_DLS2_TO_FILE_CREATE_STORES: g_value_set_boolean(value, converter->create_stores); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void _dls2_to_file_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchConverterDLS2ToFile *converter = (IpatchConverterDLS2ToFile *)object; switch(property_id) { case PROP_DLS2_TO_FILE_CREATE_STORES: converter->create_stores = g_value_get_boolean(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } CONVERTER_CLASS_INIT(file_to_dls2) CONVERTER_CLASS_INIT(file_to_dls2_sample) CONVERTER_GET_TYPE(dls2_to_file, DLS2ToFile) CONVERTER_GET_TYPE(file_to_dls2, FileToDLS2) CONVERTER_GET_TYPE(file_to_dls2_sample, FileToDLS2Sample) libinstpatch-1.1.6/libinstpatch/IpatchConvert_DLS2.h000066400000000000000000000035331400263525300224010ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_CONVERT_DLS2_H__ #define __IPATCH_CONVERT_DLS2_H__ #include #include #include typedef struct { IpatchConverter parent_instance; /*< private >*/ gboolean create_stores; } IpatchConverterDLS2ToFile; typedef IpatchConverterClass IpatchConverterDLS2ToFileClass; typedef IpatchConverter IpatchConverterFileToDLS2; typedef IpatchConverterClass IpatchConverterFileToDLS2Class; typedef IpatchConverter IpatchConverterFileToDLS2Sample; typedef IpatchConverterClass IpatchConverterFileToDLS2SampleClass; #define IPATCH_TYPE_CONVERTER_DLS2_TO_FILE \ (ipatch_converter_dls2_to_file_get_type ()) #define IPATCH_TYPE_CONVERTER_FILE_TO_DLS2 \ (ipatch_converter_file_to_dls2_get_type ()) #define IPATCH_TYPE_CONVERTER_FILE_TO_DLS2_SAMPLE \ (ipatch_converter_file_to_dls2_sample_get_type ()) GType ipatch_converter_dls2_to_file_get_type(void); GType ipatch_converter_file_to_dls2_get_type(void); GType ipatch_converter_file_to_dls2_sample_get_type(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchConvert_Gig.c000066400000000000000000000107331400263525300223760ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchConvert_Gig * @short_description: GigaSampler conversion handlers * @see_also: #IpatchConverter * @stability: Stable * * Conversion handlers for GigaSampler objects. */ #include #include #include #include "misc.h" #include "IpatchConverter_priv.h" #include "IpatchConvert_Gig.h" #include "IpatchGig.h" #include "IpatchGigFile.h" #include "IpatchDLSReader.h" #include "IpatchDLSWriter.h" #include "IpatchBase.h" #include "IpatchSampleData.h" #include "IpatchSndFile.h" #include "IpatchSampleStoreSndFile.h" #include "i18n.h" /* * GigaSampler conversion handlers (DLS is master format): * IpatchGig <==> IpatchGigFile * IpatchSampleFile => IpatchGigSample * IpatchGig <==> IpatchDLS2 * IpatchGigInst <==> IpatchDLS2Inst * IpatchGigRegion <==> IpatchDLS2Region */ /* defined in IpatchConvert_DLS2.c */ gboolean _file_to_dls2_sample_convert(IpatchConverter *converter, GError **err); /** * _ipatch_convert_gig_init: (skip) * * Init routine for GigaSampler conversion types */ void _ipatch_convert_gig_init(void) { g_type_class_ref(IPATCH_TYPE_CONVERTER_GIG_TO_FILE); ipatch_register_converter_map(IPATCH_TYPE_CONVERTER_GIG_TO_FILE, 0, 0, IPATCH_TYPE_GIG, 0, 1, IPATCH_TYPE_GIG_FILE, IPATCH_TYPE_FILE, 1); g_type_class_ref(IPATCH_TYPE_CONVERTER_FILE_TO_GIG); ipatch_register_converter_map(IPATCH_TYPE_CONVERTER_FILE_TO_GIG, 0, 0, IPATCH_TYPE_GIG_FILE, 0, 1, IPATCH_TYPE_GIG, IPATCH_TYPE_BASE, 0); g_type_class_ref(IPATCH_TYPE_CONVERTER_FILE_TO_GIG_SAMPLE); ipatch_register_converter_map(IPATCH_TYPE_CONVERTER_FILE_TO_GIG_SAMPLE, 0, 0, IPATCH_TYPE_SND_FILE, 0, 1, IPATCH_TYPE_GIG_SAMPLE, 0, 1); } /* =============== * Convert methods * =============== */ static gboolean _gig_to_file_convert(IpatchConverter *converter, GError **err) { IpatchGig *gig; IpatchGigFile *file; IpatchFileHandle *handle; IpatchDLSWriter *writer; int retval; gig = IPATCH_GIG(IPATCH_CONVERTER_INPUT(converter)); file = IPATCH_GIG_FILE(IPATCH_CONVERTER_OUTPUT(converter)); handle = ipatch_file_open(IPATCH_FILE(file), NULL, "w", err); if(!handle) { return (FALSE); } /* ++ ref new writer */ writer = ipatch_dls_writer_new(handle, IPATCH_DLS2(gig)); retval = ipatch_dls_writer_save(writer, err); g_object_unref(writer); /* -- unref writer */ return (retval); } static gboolean _file_to_gig_convert(IpatchConverter *converter, GError **err) { IpatchDLS2 *dls; IpatchGigFile *file; IpatchFileHandle *handle; IpatchDLSReader *reader; file = IPATCH_GIG_FILE(IPATCH_CONVERTER_INPUT(converter)); handle = ipatch_file_open(IPATCH_FILE(file), NULL, "r", err); if(!handle) { return (FALSE); } /* ++ ref new reader */ reader = ipatch_dls_reader_new(handle); dls = ipatch_dls_reader_load(reader, err); /* ++ ref loaded DLS object */ g_object_unref(reader); /* -- unref reader */ if(dls) { ipatch_converter_add_output(converter, G_OBJECT(dls)); g_object_unref(dls); /* -- unref loaded Gig object */ return (TRUE); } else { return (FALSE); } } #define _file_to_gig_sample_convert _file_to_dls2_sample_convert; CONVERTER_CLASS_INIT(gig_to_file) CONVERTER_GET_TYPE(gig_to_file, GigToFile) CONVERTER_CLASS_INIT(file_to_gig); CONVERTER_GET_TYPE(file_to_gig, FileToGig) CONVERTER_CLASS_INIT(file_to_gig_sample) CONVERTER_GET_TYPE(file_to_gig_sample, FileToGigSample) libinstpatch-1.1.6/libinstpatch/IpatchConvert_Gig.h000066400000000000000000000033721400263525300224040ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_CONVERT_GIG_H__ #define __IPATCH_CONVERT_GIG_H__ #include #include #include typedef IpatchConverter IpatchConverterGigToFile; typedef IpatchConverterClass IpatchConverterGigToFileClass; typedef IpatchConverter IpatchConverterFileToGig; typedef IpatchConverterClass IpatchConverterFileToGigClass; typedef IpatchConverter IpatchConverterFileToGigSample; typedef IpatchConverterClass IpatchConverterFileToGigSampleClass; #define IPATCH_TYPE_CONVERTER_GIG_TO_FILE \ (ipatch_converter_gig_to_file_get_type ()) #define IPATCH_TYPE_CONVERTER_FILE_TO_GIG \ (ipatch_converter_file_to_gig_get_type ()) #define IPATCH_TYPE_CONVERTER_FILE_TO_GIG_SAMPLE \ (ipatch_converter_file_to_gig_sample_get_type ()) GType ipatch_converter_gig_to_file_get_type(void); GType ipatch_converter_file_to_gig_get_type(void); GType ipatch_converter_file_to_gig_sample_get_type(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchConvert_SF2.c000066400000000000000000000327011400263525300222610ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchConvert_SF2 * @short_description: SoundFont conversion handlers * @see_also: #IpatchConverter * @stability: Stable * * Conversion handlers for SoundFont objects. */ #include #include #include #include #include "misc.h" #include "IpatchConvert_SF2.h" #include "IpatchConverter.h" #include "IpatchConverter_priv.h" #include "IpatchSndFile.h" #include "IpatchSF2.h" #include "IpatchSF2Gen.h" #include "IpatchSF2Inst.h" #include "IpatchSF2IZone.h" #include "IpatchSF2Reader.h" #include "IpatchSF2Mod.h" #include "IpatchSF2Preset.h" #include "IpatchSF2PZone.h" #include "IpatchSF2Sample.h" #include "IpatchSF2Writer.h" #include "IpatchDLS2.h" #include "IpatchDLS2Conn.h" #include "IpatchDLS2Inst.h" #include "IpatchDLS2Region.h" #include "IpatchDLS2Sample.h" #include "IpatchSample.h" #include "IpatchSampleData.h" #include "IpatchSampleStoreSndFile.h" #include "IpatchSampleStoreVirtual.h" #include "IpatchSampleList.h" #include "IpatchBase.h" #include "IpatchFile.h" #include "i18n.h" enum { PROP_0, PROP_SF2_TO_FILE_CREATE_STORES }; /* * SoundFont conversion handlers * IpatchSF2 <==> IpatchSF2File * IpatchSndFile => IpatchSF2Sample */ static IpatchSF2Sample *create_sf2_sample_helper(IpatchSampleStoreSndFile *store, gboolean left); static void _sf2_to_file_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void _sf2_to_file_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); /** * _ipatch_convert_SF2_init: (skip) * * Init routine for SF2 conversion types */ void _ipatch_convert_SF2_init(void) { g_type_class_ref(IPATCH_TYPE_CONVERTER_SF2_TO_FILE); g_type_class_ref(IPATCH_TYPE_CONVERTER_FILE_TO_SF2); g_type_class_ref(IPATCH_TYPE_CONVERTER_FILE_TO_SF2_SAMPLE); ipatch_register_converter_map(IPATCH_TYPE_CONVERTER_SF2_TO_FILE, 0, 0, IPATCH_TYPE_SF2, 0, 1, IPATCH_TYPE_SF2_FILE, IPATCH_TYPE_FILE, 1); ipatch_register_converter_map(IPATCH_TYPE_CONVERTER_FILE_TO_SF2, 0, 0, IPATCH_TYPE_SF2_FILE, 0, 1, IPATCH_TYPE_SF2, IPATCH_TYPE_BASE, 0); ipatch_register_converter_map(IPATCH_TYPE_CONVERTER_FILE_TO_SF2_SAMPLE, 0, 0, IPATCH_TYPE_SND_FILE, 0, 1, IPATCH_TYPE_SF2_SAMPLE, 0, 0); } /* =============== * Convert methods * =============== */ static gboolean _sf2_to_file_convert(IpatchConverter *converter, GError **err) { IpatchSF2 *sfont; IpatchSF2File *file; IpatchFileHandle *handle; IpatchSF2Writer *writer; gboolean create_stores; IpatchList *stores; int retval; sfont = IPATCH_SF2(IPATCH_CONVERTER_INPUT(converter)); file = IPATCH_SF2_FILE(IPATCH_CONVERTER_OUTPUT(converter)); handle = ipatch_file_open(IPATCH_FILE(file), NULL, "w", err); if(!handle) { return (FALSE); } writer = ipatch_sf2_writer_new(handle, sfont); /* ++ ref new writer */ retval = ipatch_sf2_writer_save(writer, err); g_object_get(converter, "create-stores", &create_stores, NULL); if(retval && create_stores) { stores = ipatch_sf2_writer_create_stores(writer); // ++ reference sample stores if(stores) { ipatch_converter_add_output(converter, G_OBJECT(stores)); g_object_unref(stores); // -- unref sample stores } } g_object_unref(writer); /* -- unref writer */ return (retval); } static gboolean _file_to_sf2_convert(IpatchConverter *converter, GError **err) { IpatchSF2 *sfont; IpatchSF2File *file; IpatchFileHandle *handle; IpatchSF2Reader *reader; file = IPATCH_SF2_FILE(IPATCH_CONVERTER_INPUT(converter)); handle = ipatch_file_open(IPATCH_FILE(file), NULL, "r", err); if(!handle) { return (FALSE); } reader = ipatch_sf2_reader_new(handle); /* ++ ref new reader */ sfont = ipatch_sf2_reader_load(reader, err); /* ++ ref loaded SoundFont */ g_object_unref(reader); /* -- unref reader */ if(sfont) { ipatch_converter_add_output(converter, G_OBJECT(sfont)); g_object_unref(sfont); /* -- unref loaded SoundFont */ return (TRUE); } else { return (FALSE); } } /* Will produce 2 samples when stereo */ static gboolean _file_to_sf2_sample_convert(IpatchConverter *converter, GError **err) { IpatchSndFile *file; IpatchSF2Sample *sf2sample, *r_sf2sample; IpatchSampleStoreSndFile *store; char *filename, *title; guint length; int format; file = IPATCH_SND_FILE(IPATCH_CONVERTER_INPUT(converter)); filename = ipatch_file_get_name(IPATCH_FILE(file)); /* ++ alloc file name */ if(!filename) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_PROGRAM, _("Sample file object must have a file name")); return (FALSE); } /* ++ ref store */ store = IPATCH_SAMPLE_STORE_SND_FILE(ipatch_sample_store_snd_file_new(filename)); if(!ipatch_sample_store_snd_file_init_read(store)) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNSUPPORTED, _("Sample file '%s' is invalid or unsupported"), filename); g_object_unref(store); /* -- unref store */ g_free(filename); /* -- free filename */ return (FALSE); } g_free(filename); /* -- free filename */ g_object_get(store, "title", &title, /* ++ alloc */ "sample-size", &length, "sample-format", &format, NULL); if(length < 4) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID, _("Sample '%s' is too small"), title ? title : _("")); g_free(title); /* -- free title */ g_object_unref(store); /* -- unref store */ return (FALSE); } g_free(title); /* -- free title */ sf2sample = create_sf2_sample_helper(store, TRUE); /* ++ ref sf2sample */ ipatch_converter_add_output(converter, (GObject *)sf2sample); /* Stereo? - Add the right channel too */ if(IPATCH_SAMPLE_FORMAT_GET_CHANNELS(format) == IPATCH_SAMPLE_STEREO) { r_sf2sample = create_sf2_sample_helper(store, FALSE); /* ++ ref r_sf2sample */ ipatch_converter_add_output(converter, (GObject *)r_sf2sample); g_object_set(sf2sample, "linked-sample", r_sf2sample, NULL); g_object_set(r_sf2sample, "linked-sample", sf2sample, NULL); g_object_unref(r_sf2sample); /* -- unref r_sf2sample */ } g_object_unref(sf2sample); /* -- unref sf2sample */ g_object_unref(store); /* -- unref store */ return (TRUE); } /* Helper function to create IpatchSF2Sample objects of mono or left/right * channels of stereo */ static IpatchSF2Sample * create_sf2_sample_helper(IpatchSampleStoreSndFile *store, gboolean left) { IpatchSampleList *samlist; IpatchSample *virtstore; IpatchSF2Sample *sf2sample; IpatchSampleData *sampledata; char newtitle[IPATCH_SFONT_NAME_SIZE + 1]; int format, rate, loop_type, root_note, fine_tune; guint length, loop_start, loop_end; char *title; int channel = IPATCH_SF2_SAMPLE_CHANNEL_MONO; g_object_get(store, "title", &title, /* ++ alloc title */ "sample-size", &length, "sample-format", &format, "sample-rate", &rate, "loop-type", &loop_type, "loop-start", &loop_start, "loop-end", &loop_end, "root-note", &root_note, "fine-tune", &fine_tune, NULL); if(title && title[0] != '\0') { strncpy(newtitle, title, IPATCH_SFONT_NAME_SIZE); newtitle[IPATCH_SFONT_NAME_SIZE] = '\0'; } else { strcpy(newtitle, _("Untitled")); } g_free(title); /* -- free title */ /* if loop not set, or messed up.. */ if(loop_type == IPATCH_SAMPLE_LOOP_NONE || loop_end <= loop_start || loop_end > length) { if(length >= 48) { loop_start = 8; loop_end = length - 8; } else /* sample is rather small */ { loop_start = 1; loop_end = length - 1; } } if(IPATCH_SAMPLE_FORMAT_GET_CHANNELS(format) == IPATCH_SAMPLE_STEREO) { /* Create sample list containing single channel of stereo */ samlist = ipatch_sample_list_new(); /* ++ alloc samlist */ ipatch_sample_list_append(samlist, (IpatchSample *)store, 0, length, left ? IPATCH_SAMPLE_LEFT : IPATCH_SAMPLE_RIGHT); /* Create virtual store for mono audio and assign the sample list to it */ virtstore = ipatch_sample_store_virtual_new(); /* ++ ref virtstore */ g_object_set(virtstore, "sample-format", format & ~IPATCH_SAMPLE_CHANNEL_MASK, "sample-rate", rate, NULL); ipatch_sample_store_virtual_set_list(IPATCH_SAMPLE_STORE_VIRTUAL(virtstore), 0, samlist); /* !! caller takes over samlist */ sampledata = ipatch_sample_data_new(); /* ++ ref sampledata */ ipatch_sample_data_add(sampledata, IPATCH_SAMPLE_STORE(virtstore)); g_object_unref(virtstore); /* -- unref virtstore */ /* FIXME - Allow for other postfixes for i18n, this could be done by * assigning strings as GObject data to the source file object */ if(strlen(newtitle) > 18) { strcpy(newtitle + 18, left ? "_L" : "_R"); } else { strcat(newtitle, left ? "_L" : "_R"); } channel = left ? IPATCH_SF2_SAMPLE_CHANNEL_LEFT : IPATCH_SF2_SAMPLE_CHANNEL_RIGHT; } else { sampledata = ipatch_sample_data_new(); /* ++ ref sampledata */ ipatch_sample_data_add(sampledata, IPATCH_SAMPLE_STORE(store)); } sf2sample = ipatch_sf2_sample_new(); /* ++ ref sf2sample */ g_object_set(sf2sample, "name", &newtitle, "sample-data", sampledata, "sample-rate", rate, "root-note", (root_note != -1) ? root_note : IPATCH_SAMPLE_ROOT_NOTE_DEFAULT, "fine-tune", fine_tune, "loop-start", loop_start, "loop-end", loop_end, "channel", channel, NULL); g_object_unref(sampledata); /* -- unref sampledata */ return (sf2sample); /* !! caller takes over reference */ } /* SF2 -> File class is handled manually to install properties */ static void _sf2_to_file_class_init(IpatchConverterClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->get_property = _sf2_to_file_get_property; obj_class->set_property = _sf2_to_file_set_property; klass->verify = NULL; klass->notes = NULL; klass->convert = _sf2_to_file_convert; g_object_class_install_property(obj_class, PROP_SF2_TO_FILE_CREATE_STORES, g_param_spec_boolean("create-stores", "Create stores", "Create sample stores", FALSE, G_PARAM_READWRITE)); } static void _sf2_to_file_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchConverterSF2ToFile *converter = (IpatchConverterSF2ToFile *)object; switch(property_id) { case PROP_SF2_TO_FILE_CREATE_STORES: g_value_set_boolean(value, converter->create_stores); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void _sf2_to_file_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchConverterSF2ToFile *converter = (IpatchConverterSF2ToFile *)object; switch(property_id) { case PROP_SF2_TO_FILE_CREATE_STORES: converter->create_stores = g_value_get_boolean(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } CONVERTER_CLASS_INIT(file_to_sf2) CONVERTER_CLASS_INIT(file_to_sf2_sample) CONVERTER_GET_TYPE(sf2_to_file, SF2ToFile) CONVERTER_GET_TYPE(file_to_sf2, FileToSF2) CONVERTER_GET_TYPE(file_to_sf2_sample, FileToSF2Sample) libinstpatch-1.1.6/libinstpatch/IpatchConvert_SF2.h000066400000000000000000000035111400263525300222630ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_CONVERT_SF2_H__ #define __IPATCH_CONVERT_SF2_H__ #include #include #include typedef struct { IpatchConverter parent_instance; /*< private >*/ gboolean create_stores; } IpatchConverterSF2ToFile; typedef IpatchConverterClass IpatchConverterSF2ToFileClass; typedef IpatchConverter IpatchConverterFileToSF2; typedef IpatchConverterClass IpatchConverterFileToSF2Class; typedef IpatchConverter IpatchConverterFileToSF2Sample; typedef IpatchConverterClass IpatchConverterFileToSF2SampleClass; #define IPATCH_TYPE_CONVERTER_SF2_TO_FILE \ (ipatch_converter_sf2_to_file_get_type ()) #define IPATCH_TYPE_CONVERTER_FILE_TO_SF2 \ (ipatch_converter_file_to_sf2_get_type ()) #define IPATCH_TYPE_CONVERTER_FILE_TO_SF2_SAMPLE \ (ipatch_converter_file_to_sf2_sample_get_type ()) GType ipatch_converter_sf2_to_file_get_type(void); GType ipatch_converter_file_to_sf2_get_type(void); GType ipatch_converter_file_to_sf2_sample_get_type(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchConvert_SLI.c000066400000000000000000000225271400263525300223230ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchConvert_SLI * @short_description: Spectralis conversion handlers * @see_also: #IpatchConverter * * Conversion handlers for Spectralis objects. */ #include #include #include #include #include "misc.h" #include "IpatchConvert_SLI.h" #include "IpatchConverter.h" #include "IpatchConverter_priv.h" #include "IpatchSndFile.h" #include "IpatchSLI.h" #include "IpatchSLIWriter.h" #include "IpatchSLIReader.h" #include "IpatchSample.h" #include "IpatchSampleData.h" #include "IpatchSampleStoreSndFile.h" #include "IpatchBase.h" #include "IpatchFile.h" #include "i18n.h" enum { PROP_0, PROP_SLI_TO_FILE_CREATE_STORES }; /* * Spectralis conversion handlers * IpatchSLI <==> IpatchSLIFile * IpatchSndFile => IpatchSLISample */ static void _sli_to_file_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void _sli_to_file_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); /** * _ipatch_convert_SLI_init: (skip) * * Init routine for SLI conversion types */ void _ipatch_convert_SLI_init(void) { g_type_class_ref(IPATCH_TYPE_CONVERTER_SLI_TO_FILE); g_type_class_ref(IPATCH_TYPE_CONVERTER_FILE_TO_SLI); g_type_class_ref(IPATCH_TYPE_CONVERTER_FILE_TO_SLI_SAMPLE); ipatch_register_converter_map(IPATCH_TYPE_CONVERTER_SLI_TO_FILE, 0, 0, IPATCH_TYPE_SLI, 0, 1, IPATCH_TYPE_SLI_FILE, IPATCH_TYPE_FILE, 1); ipatch_register_converter_map(IPATCH_TYPE_CONVERTER_FILE_TO_SLI, 0, 0, IPATCH_TYPE_SLI_FILE, 0, 1, IPATCH_TYPE_SLI, IPATCH_TYPE_BASE, 0); ipatch_register_converter_map(IPATCH_TYPE_CONVERTER_FILE_TO_SLI_SAMPLE, 0, 0, IPATCH_TYPE_SND_FILE, 0, 1, IPATCH_TYPE_SLI_SAMPLE, 0, 1); } /* =============== * Convert methods * =============== */ static gboolean _sli_to_file_convert(IpatchConverter *converter, GError **err) { IpatchSLI *sli; IpatchSLIFile *file; IpatchFileHandle *handle; IpatchSLIWriter *writer; gboolean create_stores; IpatchList *stores; int retval; sli = IPATCH_SLI(IPATCH_CONVERTER_INPUT(converter)); file = IPATCH_SLI_FILE(IPATCH_CONVERTER_OUTPUT(converter)); handle = ipatch_file_open(IPATCH_FILE(file), NULL, "w", err); if(!handle) { return (FALSE); } writer = ipatch_sli_writer_new(handle, sli); /* ++ ref new writer */ retval = ipatch_sli_writer_save(writer, err); g_object_get(converter, "create-stores", &create_stores, NULL); if(retval && create_stores) { /* ++ reference sample stores */ stores = ipatch_sli_writer_create_stores(writer); if(stores) { ipatch_converter_add_output(converter, G_OBJECT(stores)); g_object_unref(stores); /* -- unref sample stores */ } } g_object_unref(writer); /* -- unref writer */ return (retval); } static gboolean _file_to_sli_convert(IpatchConverter *converter, GError **err) { IpatchSLI *sli; IpatchSLIFile *file; IpatchFileHandle *handle; IpatchSLIReader *reader; file = IPATCH_SLI_FILE(IPATCH_CONVERTER_INPUT(converter)); handle = ipatch_file_open(IPATCH_FILE(file), NULL, "r", err); if(!handle) { return (FALSE); } reader = ipatch_sli_reader_new(handle); /* ++ ref new reader */ sli = ipatch_sli_reader_load(reader, err); /* ++ ref loaded SLI object */ g_object_unref(reader); /* -- unref reader */ if(sli) { ipatch_converter_add_output(converter, G_OBJECT(sli)); g_object_unref(sli); /* -- unref loaded SLI */ return (TRUE); } else { return (FALSE); } } static gboolean _file_to_sli_sample_convert(IpatchConverter *converter, GError **err) { IpatchSndFile *file; IpatchSLISample *slisample; IpatchSampleStoreSndFile *store; IpatchSampleData *sampledata; int format, rate, loop_type, root_note, fine_tune; guint length, loop_start, loop_end; char *filename, *title; file = IPATCH_SND_FILE(IPATCH_CONVERTER_INPUT(converter)); slisample = IPATCH_SLI_SAMPLE(IPATCH_CONVERTER_OUTPUT(converter)); filename = ipatch_file_get_name(IPATCH_FILE(file)); /* ++ alloc file name */ if(!filename) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_PROGRAM, _("Sample file object must have a file name")); return (FALSE); } /* ++ ref store */ store = IPATCH_SAMPLE_STORE_SND_FILE(ipatch_sample_store_snd_file_new(filename)); if(!ipatch_sample_store_snd_file_init_read(store)) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNSUPPORTED, _("Sample file '%s' is invalid or unsupported"), filename); g_free(filename); /* -- free filename */ g_object_unref(store); /* -- unref store */ return (FALSE); } g_free(filename); /* -- free filename */ g_object_get(store, "title", &title, /* ++ alloc title */ "sample-size", &length, "sample-format", &format, "sample-rate", &rate, "loop-type", &loop_type, "loop-start", &loop_start, "loop-end", &loop_end, "root-note", &root_note, "fine-tune", &fine_tune, NULL); if(length < 4) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID, _("Sample '%s' is too small"), title ? title : _("")); goto bad; } sampledata = ipatch_sample_data_new(); /* ++ ref sampledata */ ipatch_sample_data_add(sampledata, IPATCH_SAMPLE_STORE(store)); g_object_unref(store); /* -- unref store */ g_object_set(slisample, "name", title, "sample-data", sampledata, "sample-rate", rate, "root-note", (root_note != -1) ? root_note : 60, "fine-tune", fine_tune, "loop-start", loop_start, "loop-end", loop_end, NULL); g_object_unref(sampledata); /* -- unref sampledata */ g_free(title); /* -- free title */ return (TRUE); bad: g_free(title); /* -- free title */ g_object_unref(store); /* -- unref store */ return (FALSE); } /* SLI -> File class is handled manually to install properties */ static void _sli_to_file_class_init(IpatchConverterClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->get_property = _sli_to_file_get_property; obj_class->set_property = _sli_to_file_set_property; klass->verify = NULL; klass->notes = NULL; klass->convert = _sli_to_file_convert; g_object_class_install_property(obj_class, PROP_SLI_TO_FILE_CREATE_STORES, g_param_spec_boolean("create-stores", "Create stores", "Create sample stores", FALSE, G_PARAM_READWRITE)); } static void _sli_to_file_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchConverterSLIToFile *converter = (IpatchConverterSLIToFile *)object; switch(property_id) { case PROP_SLI_TO_FILE_CREATE_STORES: g_value_set_boolean(value, converter->create_stores); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void _sli_to_file_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchConverterSLIToFile *converter = (IpatchConverterSLIToFile *)object; switch(property_id) { case PROP_SLI_TO_FILE_CREATE_STORES: converter->create_stores = g_value_get_boolean(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } CONVERTER_CLASS_INIT(file_to_sli) CONVERTER_CLASS_INIT(file_to_sli_sample) CONVERTER_GET_TYPE(sli_to_file, SLIToFile) CONVERTER_GET_TYPE(file_to_sli, FileToSLI) CONVERTER_GET_TYPE(file_to_sli_sample, FileToSLISample) libinstpatch-1.1.6/libinstpatch/IpatchConvert_SLI.h000066400000000000000000000036221400263525300223230ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_CONVERT_SLI_H__ #define __IPATCH_CONVERT_SLI_H__ #include #include #include typedef struct { IpatchConverter parent_instance; /*< private >*/ gboolean create_stores; } IpatchConverterSLIToFile; typedef IpatchConverterClass IpatchConverterSLIToFileClass; typedef IpatchConverter IpatchConverterFileToSLI; typedef IpatchConverterClass IpatchConverterFileToSLIClass; typedef IpatchConverter IpatchConverterFileToSLISample; typedef IpatchConverterClass IpatchConverterFileToSLISampleClass; #define IPATCH_TYPE_CONVERTER_SLI_TO_FILE \ (ipatch_converter_sli_to_file_get_type ()) #define IPATCH_TYPE_CONVERTER_FILE_TO_SLI \ (ipatch_converter_file_to_sli_get_type ()) #define IPATCH_TYPE_CONVERTER_FILE_TO_SLI_SAMPLE \ (ipatch_converter_file_to_sli_sample_get_type ()) GType ipatch_converter_sli_to_file_get_type(void); GType ipatch_converter_file_to_sli_get_type(void); GType ipatch_converter_file_to_sli_sample_get_type(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchConverter.c000066400000000000000000001425331400263525300221430ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchConverter * @short_description: Base class for object conversion handlers * @see_also: * @stability: Stable * * A base abstract type for object conversion handlers. */ #include #include #include #include "misc.h" #include "IpatchConverter.h" #include "i18n.h" enum { PROP_0, PROP_PROGRESS }; /* structure used for log entries (the log is a prepend log, newest items at the head of the list) */ typedef struct _LogEntry { GObject *item; /* item this message applies to or %NULL */ guint8 type; /* type of message and flags (IpatchConverterLogType) */ union { char *msg; /* LOG_INFO/WARN/CRITICAL/FATAL */ float rating; /* LOG_RATING */ } data; } LogEntry; static void _ipatch_converter_free_converter_info(IpatchConverterInfo *data, gpointer user_data); static gint priority_GCompareFunc(gconstpointer a, gconstpointer b); static const IpatchConverterInfo *convert_lookup_map_U(GType **array, GType conv_type, GType src_type, GType dest_type, guint flags); static void ipatch_converter_class_init(IpatchConverterClass *klass); static void ipatch_converter_finalize(GObject *gobject); static void ipatch_converter_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_converter_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); /* lock used for list and hash tables */ G_LOCK_DEFINE_STATIC(conv_maps); static GList *conv_maps = NULL; /* list of all registered IpatchConverterInfo */ static gpointer parent_class = NULL; /*------ Initialization/deinitialization of converter system ----------------*/ /* Initialize converter system (conv_maps static list) */ void _ipatch_converter_init(void) { /* list of all registered IpatchConverterInfo */ conv_maps = NULL; } /* Free converter system */ void _ipatch_converter_deinit(void) { /* free list of all registered IpatchConverterInfo */ g_list_foreach(conv_maps, (GFunc)_ipatch_converter_free_converter_info, NULL); g_list_free(conv_maps); } /* free one conv_maps data */ static void _ipatch_converter_free_converter_info(IpatchConverterInfo *data, gpointer user_data) { g_slice_free(IpatchConverterInfo,data); } /*------ Converter system API ----------------------------------------------*/ /** * ipatch_convert_objects: * @input: Input object * @output: Output object * @err: Location to store error info or %NULL * * A convenience function for converting from one object to another. This * function will only work for converters which take exactly one input and * output object. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set) */ gboolean ipatch_convert_objects(GObject *input, GObject *output, GError **err) { IpatchConverter *conv; conv = ipatch_create_converter_for_objects(input, output, err); // ++ ref converter if(!conv) { return FALSE; } if(!ipatch_converter_convert(conv, err)) // -- unref converter { g_object_unref(conv); return (FALSE); } g_object_unref(conv); // -- unref converter return (TRUE); } /** * ipatch_convert_object_to_type: * @object: Object to convert from * @type: Type of object to convert to * @err: Location to store error info or %NULL to ignore * * A convenience function to convert an object to another object of a given * type. This function will only work for converters which require 1 * input and one or zero outputs. The output object is created as needed * and returned. * * Returns: (transfer full): The output object or %NULL on error (in which * case @err may be set). The returned object has a refcount of 1 which the caller owns. */ GObject * ipatch_convert_object_to_type(GObject *object, GType type, GError **err) { const IpatchConverterInfo *info; IpatchConverter *conv; GObject *output = NULL; GType convtype; convtype = ipatch_find_converter(G_OBJECT_TYPE(object), type); if(!convtype) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNHANDLED_CONVERSION, _("Unsupported conversion of type %s to %s"), G_OBJECT_TYPE_NAME(object), g_type_name(type)); return (NULL); } info = ipatch_lookup_converter_info(convtype, G_OBJECT_TYPE(object), type); g_return_val_if_fail(info != NULL, NULL); /* shouldn't happen */ if(info->dest_count < 0 || info->dest_count > 1) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNSUPPORTED, _("Conversion from %s to %s requires %d outputs"), G_OBJECT_TYPE_NAME(object), g_type_name(type), info->dest_count); return (NULL); } conv = IPATCH_CONVERTER(g_object_new(convtype, NULL)); /* ++ ref */ ipatch_converter_add_input(conv, object); if(info->dest_count == 1) /* if 1 output object expected, create it */ { output = g_object_new(type, NULL); /* ++ ref */ ipatch_converter_add_output(conv, output); } if(!ipatch_converter_convert(conv, err)) /* do the conversion */ { if(output) { g_object_unref(output); } g_object_unref(conv); return (NULL); } if(!output) { output = ipatch_converter_get_output(conv); /* ++ ref */ } g_object_unref(conv); return (output); /* !! caller takes over reference */ } /** * ipatch_convert_object_to_type_multi: (skip) * @object: Object to convert from * @type: Type of object to convert to * @err: Location to store error info or %NULL to ignore * * A convenience function to convert an object to one or more objects of a given * @type. This function will work for converters which require 1 * input and any number of outputs. * * Returns: (transfer full): List of output objects or %NULL on error (in which * case @err may be set). The returned object list has a refcount of 1 which the caller owns. */ IpatchList * ipatch_convert_object_to_type_multi(GObject *object, GType type, GError **err) { return (ipatch_convert_object_to_type_multi_set(object, type, err, NULL)); } /** * ipatch_convert_object_to_type_multi_list: (rename-to ipatch_convert_object_to_type_multi) * @object: Object to convert from * @type: Type of object to convert to * @err: Location to store error info or %NULL to ignore * * A convenience function to convert an object to one or more objects of a given * @type. This function will work for converters which require 1 * input and any number of outputs. * * Returns: (element-type GObject.Object) (transfer full): List of output objects or %NULL on error (in which * case @err may be set). Free the list with ipatch_glist_unref_free() when finished using it. * * Since: 1.1.0 */ GList * ipatch_convert_object_to_type_multi_list(GObject *object, GType type, GError **err) { /* Note: empty is intentionally not initialized. It allows to fix some build on ARM and PPC. It is save to use, because empty is not accessed in ipatch_convert_object_to_type_multi_set_vlist when the second last argument is NULL. */ va_list empty; return (ipatch_convert_object_to_type_multi_set_vlist(object, type, err, NULL, empty)); } /** * ipatch_convert_object_to_type_multi_set: (skip) * @object: Object to convert from * @type: Type of object to convert to * @err: Location to store error info or %NULL to ignore * @first_property_name: (nullable): Name of first property to assign or %NULL * @...: First property value followed by property name/value pairs (as per * g_object_set()) to assign to the resulting converter, terminated with a * %NULL property name. * * A convenience function to convert an object to one or more objects of a given * @type. This function will work for converters which require 1 * input and any number of outputs. Like ipatch_convert_object_to_type_multi() * but allows for properties of the converter to be assigned. * * Returns: (transfer full): List of output objects or %NULL on error (in which * case @err may be set). The returned object list has a refcount of 1 which the caller owns. */ IpatchList * ipatch_convert_object_to_type_multi_set(GObject *object, GType type, GError **err, const char *first_property_name, ...) { IpatchList *list; GList *items; va_list args; va_start(args, first_property_name); items = ipatch_convert_object_to_type_multi_set_vlist(object, type, // ++ alloc items list err, first_property_name, args); va_end(args); if(!items) { return NULL; } list = ipatch_list_new(); // ++ ref new list list->items = items; // !! Assign items to list return list; // !! Caller takes over list } /** * ipatch_convert_object_to_type_multi_set_vlist: (skip) * @object: Object to convert from * @type: Type of object to convert to * @err: Location to store error info or %NULL to ignore * @first_property_name: (nullable): Name of first property to assign or %NULL * @args: First property value followed by property name/value pairs (as per * g_object_set()) to assign to the resulting converter, terminated with a * %NULL property name. * * A convenience function to convert an object to one or more objects of a given * @type. This function will work for converters which require 1 * input and any number of outputs. Like ipatch_convert_object_to_type_multi() * but allows for properties of the converter to be assigned. * * Returns: (element-type GObject.Object) (transfer full): List of output objects or %NULL on error (in which * case @err may be set). Free the list with ipatch_glist_unref_free() when finished using it. * * Since: 1.1.0 */ GList * ipatch_convert_object_to_type_multi_set_vlist(GObject *object, GType type, GError **err, const char *first_property_name, va_list args) { IpatchConverter *conv; GList *items; conv = ipatch_create_converter_for_object_to_type(object, type, err); // ++ ref converter if(!conv) { return NULL; } /* assign properties (if any) */ if(first_property_name) { g_object_set_valist((GObject *)conv, first_property_name, args); } if(!ipatch_converter_convert(conv, err)) /* do the conversion */ { g_object_unref(conv); // -- unref converter return (NULL); } items = ipatch_converter_get_outputs_list(conv); /* ++ alloc object list */ g_object_unref(conv); /* -- unref converter */ return (items); /* !! caller takes over ownership */ } /** * ipatch_create_converter: * @src_type: #GObject derived source type * @dest_type: #GObject derived destination type * * Create a converter object for converting an object of type @src_type to * @dest_type. A convenience function, since one could use * ipatch_find_converter() and create an instance of the returned type. * See ipatch_find_converter() for more details. * * Returns: (transfer full): The new converter object with a reference count * of 1 which the caller owns, or %NULL if there is no matching conversion * handler type. */ IpatchConverter * ipatch_create_converter(GType src_type, GType dest_type) { GType conv_type; g_return_val_if_fail(g_type_is_a(src_type, G_TYPE_OBJECT), NULL); g_return_val_if_fail(g_type_is_a(dest_type, G_TYPE_OBJECT), NULL); conv_type = ipatch_find_converter(src_type, dest_type); if(!conv_type) { return (NULL); } /* ++ ref new object and let the caller have it */ return (IPATCH_CONVERTER(g_object_new(conv_type, NULL))); } /** * ipatch_create_converter_for_objects: * @input: Input object * @output: Output object * @err: Location to store error info or %NULL * * A convenience function for creating a converter for converting from one * object to another. This function will only work for converters which take * exactly one input and output object. * * Returns: (transfer full): The new converter or %NULL on error * * Since: 1.1.0 */ IpatchConverter * ipatch_create_converter_for_objects(GObject *input, GObject *output, GError **err) { IpatchConverter *conv; g_return_val_if_fail(G_IS_OBJECT(input), FALSE); g_return_val_if_fail(G_IS_OBJECT(output), FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* ++ ref new converter */ conv = ipatch_create_converter(G_OBJECT_TYPE(input), G_OBJECT_TYPE(output)); if(!conv) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNHANDLED_CONVERSION, _("Unsupported conversion of type %s to %s."), G_OBJECT_TYPE_NAME(input), G_OBJECT_TYPE_NAME(output)); return (FALSE); } ipatch_converter_add_input(conv, input); ipatch_converter_add_output(conv, output); return (conv); // !! Caller takes over reference } /** * ipatch_create_converter_for_object_to_type: * @object: Object to convert from * @dest_type: Type of object to convert to * @err: Location to store error info or %NULL to ignore * * A convenience function to create a converter for converting an object to * another object of a given type. * * Returns: (transfer full): The new converter object with a reference count * of 1 which the caller owns, or %NULL if there is no matching conversion * handler type. * * Since: 1.1.0 */ IpatchConverter * ipatch_create_converter_for_object_to_type(GObject *object, GType dest_type, GError **err) { const IpatchConverterInfo *info; IpatchConverter *conv; GObject *output = NULL; GType convtype; int i; convtype = ipatch_find_converter(G_OBJECT_TYPE(object), dest_type); if(!convtype) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNHANDLED_CONVERSION, _("Unsupported conversion of type %s to %s"), G_OBJECT_TYPE_NAME(object), g_type_name(dest_type)); return (NULL); } info = ipatch_lookup_converter_info(convtype, G_OBJECT_TYPE(object), dest_type); g_return_val_if_fail(info != NULL, NULL); /* shouldn't happen */ conv = IPATCH_CONVERTER(g_object_new(convtype, NULL)); /* ++ ref */ ipatch_converter_add_input(conv, object); if(info->dest_count > 0) /* if outputs are expected, create them */ { for(i = 0; i < info->dest_count; i++) { output = g_object_new(dest_type, NULL); /* ++ ref output */ ipatch_converter_add_output(conv, output); g_object_unref(output); /* -- unref output */ } } return (conv); /* !! caller takes over reference */ } /** * ipatch_register_converter_map: * @conv_type: #IpatchConverter derived GType of conversion handler * @flags: #IpatchConverterFlags, #IPATCH_CONVERTER_SRC_DERIVED or * #IPATCH_CONVERTER_DEST_DERIVED will match derived types of @src_type * or @dest_type respectively. * @priority: Converter map priority (number from 0 to 100, 0 will use the default). * #IpatchConverterPriority defines some common priorities. Used for overriding other * converters for specific types. * @src_type: Type for source object (GObject derived type or interface type). * @src_match: The furthest parent type of @src_type to match (all types in * between are also matched, 0 or G_TYPE_NONE to only use @src_type). * @src_count: Required number of source items (can also be an #IpatchConverterCount value) * @dest_type: Type for destination object (GObject derived type or interface type). * @dest_match: The furthest parent type of @dest_type to match (all types in * between are also matched, 0, or G_TYPE_NONE to only use @dest_type). * @dest_count: Required number of destination items (can also be * an #IpatchConverterCount value). This value can be 0 in the case where * no objects should be supplied, but will be instead assigned after * conversion. * * Registers a #IpatchConverter handler to convert objects of @src_type to @dest_type. * NOTE: Previous versions of this function combined flags and priority into a single param. * * Since: 1.1.0 */ void ipatch_register_converter_map(GType conv_type, guint8 flags, guint8 priority, GType src_type, GType src_match, gint8 src_count, GType dest_type, GType dest_match, gint8 dest_count) { const IpatchConverterInfo *converter_exists; IpatchConverterInfo *map; g_return_if_fail(g_type_is_a(conv_type, IPATCH_TYPE_CONVERTER)); g_return_if_fail(g_type_is_a(src_type, G_TYPE_OBJECT) || G_TYPE_IS_INTERFACE(src_type)); g_return_if_fail(g_type_is_a(dest_type, G_TYPE_OBJECT) || G_TYPE_IS_INTERFACE(dest_type)); g_return_if_fail(!src_match || g_type_is_a(src_type, src_match)); g_return_if_fail(!dest_match || g_type_is_a(dest_type, dest_match)); converter_exists = ipatch_lookup_converter_info(conv_type, 0, 0); g_return_if_fail(!converter_exists); priority = flags & 0xFF; if(priority == 0) { priority = IPATCH_CONVERTER_PRIORITY_DEFAULT; } // Enable derived flags for interface types if(G_TYPE_IS_INTERFACE(src_type)) { flags |= IPATCH_CONVERTER_SRC_DERIVED; } if(G_TYPE_IS_INTERFACE(dest_type)) { flags |= IPATCH_CONVERTER_DEST_DERIVED; } map = g_slice_new(IpatchConverterInfo); map->conv_type = conv_type; map->flags = flags; map->priority = priority; map->src_type = src_type; map->src_match = src_match; map->src_count = src_count; map->dest_type = dest_type; map->dest_match = dest_match; map->dest_count = dest_count; G_LOCK(conv_maps); conv_maps = g_list_insert_sorted(conv_maps, map, priority_GCompareFunc); G_UNLOCK(conv_maps); } /* GList GCompareFunc to sort list by mapping priority */ static gint priority_GCompareFunc(gconstpointer a, gconstpointer b) { IpatchConverterInfo *mapa = (IpatchConverterInfo *)a, *mapb = (IpatchConverterInfo *)b; /* priority sorts from highest to lowest, so subtract a from b */ return (mapb->priority - mapa->priority); } /** * ipatch_find_converter: * @src_type: #GObject derived source type (0 or G_TYPE_NONE for wildcard - Since 1.1.0) * @dest_type: #GObject derived destination type (0 or G_TYPE_NONE for wildcard - Since 1.1.0) * * Lookup a conversion handler type for a given @src_type to @dest_type * conversion. In some cases there may be multiple conversion handlers for * the given types, this function only returns the highest priority type. * To get a list of all available converters use ipatch_find_converters(). * * Returns: An #IpatchConverter derived GType of the matching conversion * handler or 0 if no matches. */ GType ipatch_find_converter(GType src_type, GType dest_type) { const IpatchConverterInfo *info; g_return_val_if_fail(g_type_is_a(src_type, G_TYPE_OBJECT) || G_TYPE_IS_INTERFACE(src_type), 0); g_return_val_if_fail(g_type_is_a(dest_type, G_TYPE_OBJECT) || G_TYPE_IS_INTERFACE(dest_type), 0); G_LOCK(conv_maps); info = convert_lookup_map_U(NULL, 0, src_type, dest_type, 0); G_UNLOCK(conv_maps); return (info ? info->conv_type : 0); } /** * ipatch_find_converters: * @src_type: #GObject derived source type (G_TYPE_NONE for wildcard) * @dest_type: #GObject derived destination type (G_TYPE_NONE for wildcard) * @flags: #IpatchConverterFlags to modify converter matching behavior (logically OR'd with registered converter flags) * * Lookup conversion handler types matching search criteria. * * Returns: (array zero-terminated=1) (transfer full) (nullable): 0 terminated array of #IpatchConverter derived GTypes or %NULL * if no matching converters. Array should be freed with g_free() when finished using it. * * Since: 1.1.0 */ GType * ipatch_find_converters(GType src_type, GType dest_type, guint flags) { GType *types_array; G_LOCK(conv_maps); convert_lookup_map_U(&types_array, 0, src_type, dest_type, flags); G_UNLOCK(conv_maps); return types_array; } /* Lookup a IpatchConverterInfo in the conv_maps list (caller responsible for LOCK of conv_maps). * Pass a pointer for "array" if all matching info is desired or NULL to ignore. * Use 0 or G_TYPE_NONE for wildcard types. * IpatchConverterInfo structures are considered static (never unregistered and don't change). */ static const IpatchConverterInfo * convert_lookup_map_U(GType **array, GType conv_type, GType src_type, GType dest_type, guint flags) { GArray *types_array = NULL; IpatchConverterInfo *info; GList *p; if(array) { *array = NULL; } if(conv_type == G_TYPE_NONE) { conv_type = 0; } if(src_type == G_TYPE_NONE) { src_type = 0; } if(dest_type == G_TYPE_NONE) { dest_type = 0; } for(p = conv_maps; p; p = p->next) { info = (IpatchConverterInfo *)(p->data); if(conv_type && conv_type != info->conv_type) { continue; } if(src_type) { if((flags | info->flags) & IPATCH_CONVERTER_SRC_DERIVED) // If derived flag set (from map or caller) and source type is not a descendant of map type, skip { // Derived will automatically be set for interface types as well if(!g_type_is_a(info->src_type, src_type)) { continue; } } else if(info->src_match) // If parent source match set and type is outside of map type range of src_match <-> src_type, skip { if(!g_type_is_a(src_type, info->src_match) || (src_type != info->src_type && g_type_is_a(src_type, info->src_type))) { continue; } } // Neither derived or src_map, just do a direct comparison else if(src_type != info->src_type) { continue; } } if(dest_type) { if((flags | info->flags) & IPATCH_CONVERTER_DEST_DERIVED) // If derived flag set and destination type is not a descendant of map type, skip { // Derived will automatically be set for interface types as well if(!g_type_is_a(info->dest_type, dest_type)) { continue; } } else if(info->dest_match) // If parent destination match set and type is outside of map type range of dest_match <-> dest_type, skip { if(!g_type_is_a(dest_type, info->dest_match) || (dest_type != info->dest_type && g_type_is_a(dest_type, info->dest_type))) { continue; } } // Neither derived or dest_map, just do a direct comparison else if(dest_type != info->dest_type) { continue; } } if(!array) // Not requesting array? Just return highest priority match { return info; } // Create types array if not already created if(!types_array) { types_array = g_array_new(TRUE, FALSE, sizeof(GType)); } g_array_append_val(types_array, info->conv_type); // Append converter type to array } /* free type array but not the array itself */ if(types_array) { *array = (GType *)g_array_free(types_array, FALSE); } return NULL; } /** * ipatch_lookup_converter_info: * @conv_type: #IpatchConverter derived GType to lookup info on (or 0 for wildcard, or G_TYPE_NONE - since 1.1.0) * @src_type: Source type of conversion map to lookup (or 0 for default map, or G_TYPE_NONE - since 1.1.0) * @dest_type: Destination type of conversion map (0 if @src_type is 0, or G_TYPE_NONE - since 1.1.0) * * Lookup converter map info. * * Returns: (transfer none) (nullable): Converter info structure or %NULL if no match. * The returned pointer is internal and should not be modified or freed. */ const IpatchConverterInfo * ipatch_lookup_converter_info(GType conv_type, GType src_type, GType dest_type) { const IpatchConverterInfo *info; G_LOCK(conv_maps); info = convert_lookup_map_U(NULL, conv_type, src_type, dest_type, 0); G_UNLOCK(conv_maps); return (info); } /** * ipatch_get_converter_info: * @conv_type: #IpatchConverter derived GType to lookup info on * * Get converter info structure for a converter type. * * Returns: (transfer none) (nullable): Converter info structure or %NULL if no match. * The returned pointer is internal and should not be modified or freed. * * Since: 1.1.0 */ const IpatchConverterInfo * ipatch_get_converter_info(GType conv_type) { return ipatch_lookup_converter_info(conv_type, 0, 0); } GType ipatch_converter_get_type(void) { static GType obj_type = 0; if(!obj_type) { static const GTypeInfo obj_info = { sizeof(IpatchConverterClass), NULL, NULL, (GClassInitFunc)ipatch_converter_class_init, NULL, NULL, sizeof(IpatchConverter), 0, (GInstanceInitFunc) NULL, }; obj_type = g_type_register_static(G_TYPE_OBJECT, "IpatchConverter", &obj_info, G_TYPE_FLAG_ABSTRACT); } return (obj_type); } static void ipatch_converter_class_init(IpatchConverterClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->set_property = ipatch_converter_set_property; obj_class->get_property = ipatch_converter_get_property; obj_class->finalize = ipatch_converter_finalize; g_object_class_install_property(obj_class, PROP_PROGRESS, g_param_spec_float("progress", _("Progress"), _("Conversion progress"), 0.0, 1.0, 0.0, G_PARAM_READWRITE)); } /* function called when a patch is being destroyed */ static void ipatch_converter_finalize(GObject *gobject) { IpatchConverter *converter = IPATCH_CONVERTER(gobject); GList *p; // Call destroy notify function for link function assignment (if set) if(converter->notify_func) { converter->notify_func(converter->user_data); } p = converter->inputs; while(p) { g_object_unref(p->data); p = g_list_delete_link(p, p); } p = converter->outputs; while(p) { g_object_unref(p->data); p = g_list_delete_link(p, p); } if(G_OBJECT_CLASS(parent_class)->finalize) { G_OBJECT_CLASS(parent_class)->finalize(gobject); } } static void ipatch_converter_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchConverter *converter; g_return_if_fail(IPATCH_IS_CONVERTER(object)); converter = IPATCH_CONVERTER(object); switch(property_id) { case PROP_PROGRESS: converter->progress = g_value_get_float(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_converter_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchConverter *converter; g_return_if_fail(IPATCH_IS_CONVERTER(object)); converter = IPATCH_CONVERTER(object); switch(property_id) { case PROP_PROGRESS: g_value_set_float(value, converter->progress); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /** * ipatch_converter_add_input: * @converter: Converter instance * @object: Object to add * * Add an input object to a converter object. */ void ipatch_converter_add_input(IpatchConverter *converter, GObject *object) { g_return_if_fail(IPATCH_IS_CONVERTER(converter)); g_return_if_fail(G_IS_OBJECT(object)); converter->inputs = g_list_append(converter->inputs, g_object_ref(object)); } /** * ipatch_converter_add_output: * @converter: Converter instance * @object: Object to add * * Add an output object to a converter object. */ void ipatch_converter_add_output(IpatchConverter *converter, GObject *object) { g_return_if_fail(IPATCH_IS_CONVERTER(converter)); g_return_if_fail(G_IS_OBJECT(object)); converter->outputs = g_list_append(converter->outputs, g_object_ref(object)); } /** * ipatch_converter_add_inputs: * @converter: Converter instance * @objects: (element-type GObject.Object) (transfer none): List of objects to add * * Add a list of input objects to a converter object. */ void ipatch_converter_add_inputs(IpatchConverter *converter, GList *objects) { GList *p; g_return_if_fail(IPATCH_IS_CONVERTER(converter)); g_return_if_fail(objects != NULL); p = objects; while(p) { converter->inputs = g_list_append(converter->inputs, g_object_ref(p->data)); p = g_list_next(p); } } /** * ipatch_converter_add_outputs: * @converter: Converter instance * @objects: (element-type GObject.Object) (transfer none): List of objects to add * * Add a list of output objects to a converter object. */ void ipatch_converter_add_outputs(IpatchConverter *converter, GList *objects) { GList *p; g_return_if_fail(IPATCH_IS_CONVERTER(converter)); g_return_if_fail(objects != NULL); p = objects; while(p) { converter->outputs = g_list_append(converter->outputs, g_object_ref(p->data)); p = g_list_next(p); } } /** * ipatch_converter_get_input: * @converter: Converter instance * * Get a single input object from a converter. * * Returns: (transfer full): The first input object from a converter or %NULL if * no input objects. The caller owns a reference to the returned * object. */ GObject * ipatch_converter_get_input(IpatchConverter *converter) { GObject *obj = NULL; g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), NULL); if(converter->inputs) { obj = (GObject *)(converter->inputs->data); } if(obj) { g_object_ref(obj); } return (obj); } /** * ipatch_converter_get_output: * @converter: Converter instance * * Get a single output object from a converter. * * Returns: (transfer full): The first output object from a converter or %NULL if * no output objects. The caller owns a reference to the returned * object. */ GObject * ipatch_converter_get_output(IpatchConverter *converter) { GObject *obj = NULL; g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), NULL); if(converter->outputs) { obj = (GObject *)(converter->outputs->data); } if(obj) { g_object_ref(obj); } return (obj); } /** * ipatch_converter_get_inputs: (skip) * @converter: Converter instance * * Get a list of input objects from a converter. * * Returns: (transfer full): A newly created input object list from a converter or * %NULL if no input objects. The caller owns a reference to the * returned list. */ IpatchList * ipatch_converter_get_inputs(IpatchConverter *converter) { IpatchList *list; GList *items; items = ipatch_converter_get_inputs_list(converter); if(!items) { return NULL; } list = ipatch_list_new(); /* ++ ref new */ list->items = items; // !! list takes over items return (list); /* !! caller takes over list reference */ } /** * ipatch_converter_get_inputs_list: (rename-to ipatch_converter_get_inputs) * @converter: Converter instance * * Get a list of input objects from a converter. * * Returns: (element-type GObject.Object) (transfer full): A newly created input * object list from a converter or %NULL if no input objects. * Free the list with ipatch_glist_unref_free() when finished using it. * * Since: 1.1.0 */ GList * ipatch_converter_get_inputs_list(IpatchConverter *converter) { GList *items = NULL, *p; g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), NULL); if(!converter->inputs) { return (NULL); } for(p = converter->inputs; p; p = g_list_next(p)) { items = g_list_prepend(items, g_object_ref(p->data)); } return g_list_reverse(items); // !! caller takes over list } /** * ipatch_converter_get_outputs: (skip) * @converter: Converter instance * * Get a list of output objects from a converter. * * Returns: (transfer full): A newly created output object list from a converter or * %NULL if no output objects. The caller owns a reference to the * returned list. */ IpatchList * ipatch_converter_get_outputs(IpatchConverter *converter) { IpatchList *list; GList *items; items = ipatch_converter_get_outputs_list(converter); if(!items) { return NULL; } list = ipatch_list_new(); /* ++ ref new */ list->items = items; // !! list takes over items return (list); /* !! caller takes over list reference */ } /** * ipatch_converter_get_outputs_list: (rename-to ipatch_converter_get_outputs) * @converter: Converter instance * * Get a list of output objects from a converter. * * Returns: (element-type GObject.Object) (transfer full): A newly created output * object list from a converter or %NULL if no output objects. * Free the list with ipatch_glist_unref_free() when finished using it. * * Since: 1.1.0 */ GList * ipatch_converter_get_outputs_list(IpatchConverter *converter) { GList *items = NULL, *p; g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), NULL); if(!converter->outputs) { return (NULL); } for(p = converter->outputs; p; p = g_list_next(p)) { items = g_list_prepend(items, g_object_ref(p->data)); } return g_list_reverse(items); // !! caller takes over list } /** * ipatch_converter_verify: * @converter: Converter object * @failmsg: (out) (transfer full): Location to store a failure message if @converter fails * verification. The stored message should be freed when no longer needed. * * Verifies the settings of a converter object. This is automatically called * before a conversion is done, so it doesn't usually need to be explicitly * called. * * Returns: %TRUE if @converter passed verification, %FALSE otherwise in which * case an error message may be stored in @failmsg. Remember to free the * message when finished with it. */ gboolean ipatch_converter_verify(IpatchConverter *converter, char **failmsg) { IpatchConverterClass *klass; const IpatchConverterInfo *info; char *msg = NULL; gboolean retval; GType type; int count; GList *p; g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), FALSE); klass = IPATCH_CONVERTER_GET_CLASS(converter); // Verify method set? - use it if(klass->verify) { retval = (klass->verify)(converter, &msg); if(failmsg) { *failmsg = msg; } else { g_free(msg); } return (retval); } // No verify method, check input/output types and counts info = ipatch_lookup_converter_info(G_OBJECT_TYPE(converter), 0, 0); if(info->src_count == 0 && converter->inputs) { goto input_failed; } for(p = converter->inputs, count = 0; p; p = p->next, count++) { type = G_OBJECT_TYPE(p->data); if(info->flags & IPATCH_CONVERTER_SRC_DERIVED) // If derived flag set and source type is not a descendant of map type, fail { if(!g_type_is_a(info->src_type, type)) { goto input_failed; } } else if(info->src_match) // If parent source match set and type is outside of map type range of src_match <-> src_type, fail { if(!g_type_is_a(type, info->src_match) || (type != info->src_type && g_type_is_a(type, info->src_type))) { goto input_failed; } } // Neither derived or src_map, just do a direct type comparison, if not equal, fail else if(type != info->src_type) { goto input_failed; } } if(info->src_count == IPATCH_CONVERTER_COUNT_ONE_OR_MORE) { if(count < 1) { goto input_failed; } } else if(info->src_count != IPATCH_CONVERTER_COUNT_ZERO_OR_MORE && count != info->src_count) { goto input_failed; } for(p = converter->outputs, count = 0; p; p = p->next, count++) { type = G_OBJECT_TYPE(p->data); if(info->flags & IPATCH_CONVERTER_DEST_DERIVED) // If derived flag set and dest type is not a descendant of map type, fail { if(!g_type_is_a(info->dest_type, type)) { goto input_failed; } } else if(info->dest_match) // If parent dest match set and type is outside of map type range of dest_match <-> dest_type, fail { if(!g_type_is_a(type, info->dest_match) || (type != info->dest_type && g_type_is_a(type, info->dest_type))) { goto input_failed; } } // Neither derived or dest_map, just do a direct type comparison, if not equal, fail else if(type != info->dest_type) { goto input_failed; } } if(info->dest_count == IPATCH_CONVERTER_COUNT_ONE_OR_MORE) { if(count < 1) { goto output_failed; } } else if(info->dest_count != IPATCH_CONVERTER_COUNT_ZERO_OR_MORE && count != info->dest_count) { goto output_failed; } return TRUE; input_failed: if(failmsg) { *failmsg = g_strdup("Converter inputs failed to verify"); } return (FALSE); output_failed: if(failmsg) { *failmsg = g_strdup("Converter outputs failed to verify"); } return (FALSE); } /** * ipatch_converter_init: * @converter: Converter object * * Allows a converter type to initialize its parameters based on its input * and/or output objects. This function should be called after setting the * input and output objects, but before setting object parameters or * converting. Calling this function is not required, but certain converters * will work more intuitively if it is (an example is an audio sample saver * converter which could initialize the output sample file object format * based on the input sample object format). * * NOTE: Verification of converter parameters is not done by this routine * so converter types implementing an init method are responsible for their * own verification. */ void ipatch_converter_init(IpatchConverter *converter) { IpatchConverterClass *klass; g_return_if_fail(IPATCH_IS_CONVERTER(converter)); klass = IPATCH_CONVERTER_GET_CLASS(converter); if(!klass->init) { return; } (klass->init)(converter); } /** * ipatch_converter_convert: * @converter: Converter object * @err: Location to store error info or %NULL * * Runs the conversion method of a converter object. The @converter object's * conversion paramters are first verified before the conversion is run. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean ipatch_converter_convert(IpatchConverter *converter, GError **err) { IpatchConverterClass *klass; char *failmsg = NULL; g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), FALSE); g_return_val_if_fail(!err || !*err, FALSE); klass = IPATCH_CONVERTER_GET_CLASS(converter); g_return_val_if_fail(klass->convert != NULL, FALSE); if(!ipatch_converter_verify(converter, &failmsg)) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID, _("Verification of conversion parameters failed: %s"), failmsg ? failmsg : _("")); return (FALSE); } return ((klass->convert)(converter, err)); } /** * ipatch_converter_reset: * @converter: Converter object * * Reset a converter object so it can be re-used. */ void ipatch_converter_reset(IpatchConverter *converter) { GList *p; g_return_if_fail(IPATCH_IS_CONVERTER(converter)); p = converter->inputs; while(p) { g_object_unref(p->data); p = g_list_delete_link(p, p); } converter->inputs = NULL; p = converter->outputs; while(p) { g_object_unref(p->data); p = g_list_delete_link(p, p); } converter->outputs = NULL; p = converter->log; while(p) { g_free(p->data); p = g_list_delete_link(p, p); } converter->log = NULL; converter->min_rate = 0.0; converter->max_rate = 0.0; converter->avg_rate = 0.0; converter->sum_rate = 0.0; converter->item_count = 0; } /** * ipatch_converter_get_notes: * @converter: Converter object * * Get notes about a conversion implementation. These notes could include * things such as information about any loss of information in the conversion * that may occur, etc. * * Returns: Newly allocated and possibly multi-line notes and comments * about a given conversion or %NULL if no notes. Meant for display to * the user. This string should be freed when no longer needed. */ char * ipatch_converter_get_notes(IpatchConverter *converter) { IpatchConverterClass *klass; g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), NULL); klass = IPATCH_CONVERTER_GET_CLASS(converter); if(!klass->notes) { return (NULL); } return ((klass->notes)(converter)); } /* FIXME-GIR: @type consists of multiple flags types? @msg can be static or dynamic. */ /** * ipatch_converter_log: (skip) * @converter: Converter object * @item: (nullable): Item the log entry pertains to or %NULL if not item specific * @type: #IpatchConverterLogType and other flags * @msg: Message of the log. If message is dynamically allocated then * the #IPATCH_CONVERTER_LOG_MSG_ALLOC flag should be set in @type * * Logs an entry to a converter log. Usually only used by converter * object handlers. */ void ipatch_converter_log(IpatchConverter *converter, GObject *item, int type, char *msg) { LogEntry *entry; g_return_if_fail(IPATCH_IS_CONVERTER(converter)); g_return_if_fail(!item || G_IS_OBJECT(item)); g_return_if_fail(msg != NULL); entry = g_new0(LogEntry, 1); if(item) { entry->item = g_object_ref(item); } entry->type = type; entry->data.msg = msg; converter->log = g_list_prepend(converter->log, entry); } /* FIXME-GIR: @type consists of multiple flags types? */ /** * ipatch_converter_log_printf: * @converter: Converter object * @item: (nullable): Item the log entry pertains to or %NULL if not item specific * @type: #IpatchConverterLogType and other flags * @fmt: Printf format style string * @...: Arguments to @fmt message string * * Logs a printf style message to a converter log. Usually only used by converter * object handlers. The #IPATCH_CONVERTER_LOG_MSG_ALLOC flag is automatically * set on the log entry, since it is dynamically allocated. */ void ipatch_converter_log_printf(IpatchConverter *converter, GObject *item, int type, const char *fmt, ...) { LogEntry *entry; va_list args; g_return_if_fail(IPATCH_IS_CONVERTER(converter)); g_return_if_fail(!item || G_IS_OBJECT(item)); g_return_if_fail(fmt != NULL); entry = g_new0(LogEntry, 1); if(item) { entry->item = g_object_ref(item); } entry->type = type | IPATCH_CONVERTER_LOG_MSG_ALLOC; va_start(args, fmt); entry->data.msg = g_strdup_vprintf(fmt, args); va_end(args); converter->log = g_list_prepend(converter->log, entry); } /** * ipatch_converter_log_next: * @converter: Converter object * @pos: (out): Opaque current position in log, should be %NULL on first call to * this function to return first log item (oldest item) * @item: (out) (transfer none) (optional): Location to store item of the log entry or %NULL, * no reference is added so the item is only guarenteed to exist for as long as the * @converter does * @type: (out) (optional): Location to store the type parameter of the log entry or %NULL * @msg: (out) (transfer none): Location to store the message of the log entry or %NULL, message * is internal and should not be messed with and is only guarenteed for * the lifetime of the @converter * * Get the first or next log entry from a converter object. * * Returns: %TRUE if an entry was returned, %FALSE if no more entries in which * case item, type and msg are all undefined. */ gboolean ipatch_converter_log_next(IpatchConverter *converter, gpointer *pos, GObject **item, int *type, char **msg) { LogEntry *entry; GList *p; g_return_val_if_fail(IPATCH_IS_CONVERTER(converter), FALSE); g_return_val_if_fail(pos != NULL, FALSE); if(!*pos) { p = g_list_last(converter->log); } else { p = g_list_previous((GList *)(*pos)); } if(!p) { return (FALSE); } entry = (LogEntry *)(p->data); if(item) { *item = entry->item; } if(type) { *type = entry->type; } if(msg) { *msg = entry->data.msg; } return (TRUE); } /** * ipatch_converter_set_link_funcs: (skip) * @converter: Converter object * @link_lookup: Set the link lookup callback function * @link_notify: Set the link notify callback function * * This function allows for object link interception by the user of * an #IpatchConverter instance. The callback functions are used by * conversion processes which create objects linking other external * objects which need to be converted. For each link object needing * conversion @link_lookup will be called. If @link_lookup returns a valid * pointer it is used as the converted link object, if %NULL is returned then * the link will be converted and @link_notify will be called with the new * converted item. An example usage of this feature is * the #IpatchPaste system, which does object conversions and substitutes * already converted objects (a conversion pool). */ void ipatch_converter_set_link_funcs(IpatchConverter *converter, IpatchConverterLinkLookupFunc *link_lookup, IpatchConverterLinkNotifyFunc *link_notify) { g_return_if_fail(IPATCH_IS_CONVERTER(converter)); ipatch_converter_set_link_funcs_full(converter, link_lookup, link_notify, NULL, NULL); } /** * ipatch_converter_set_link_funcs_full: (rename-to ipatch_converter_set_link_funcs) * @converter: Converter object * @link_lookup: (scope notified) (nullable): Set the link lookup callback function * @link_notify: (scope notified) (nullable): Set the link notify callback function * @notify_func: (scope async) (closure user_data) (nullable): Callback which gets * called when link functions are removed. * @user_data: (nullable): User data passed to @notify_func (not @link_lookup or @link_notify) * * This function allows for object link interception by the user of * an #IpatchConverter instance. The callback functions are used by * conversion processes which create objects linking other external * objects which need to be converted. For each link object needing * conversion @link_lookup will be called. If @link_lookup returns a valid * pointer it is used as the converted link object, if %NULL is returned then * the link will be converted and @link_notify will be called with the new * converted item. An example usage of this feature is * the #IpatchPaste system, which does object conversions and substitutes * already converted objects (a conversion pool). * * Since: 1.1.0 */ void ipatch_converter_set_link_funcs_full(IpatchConverter *converter, IpatchConverterLinkLookupFunc *link_lookup, IpatchConverterLinkNotifyFunc *link_notify, GDestroyNotify notify_func, gpointer user_data) { g_return_if_fail(IPATCH_IS_CONVERTER(converter)); if(converter->notify_func) { converter->notify_func(converter->user_data); } converter->link_lookup = link_lookup; converter->link_notify = link_notify; converter->notify_func = notify_func; converter->user_data = user_data; } libinstpatch-1.1.6/libinstpatch/IpatchConverter.h000066400000000000000000000271471400263525300221530ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_CONVERTER_H__ #define __IPATCH_CONVERTER_H__ #include #include #include #include /* forward type declarations */ typedef struct _IpatchConverter IpatchConverter; typedef struct _IpatchConverterClass IpatchConverterClass; typedef struct _IpatchConverterInfo IpatchConverterInfo; #define IPATCH_TYPE_CONVERTER (ipatch_converter_get_type ()) #define IPATCH_CONVERTER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_CONVERTER, IpatchConverter)) #define IPATCH_CONVERTER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_CONVERTER, \ IpatchConverterClass)) #define IPATCH_IS_CONVERTER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_CONVERTER)) #define IPATCH_IS_CONVERTER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_CONVERTER)) #define IPATCH_CONVERTER_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_CONVERTER, \ IpatchConverterClass)) /** * IpatchConverterLinkLookupFunc: * @converter: Converter instance * @item: Converted item which has the link property that will be assigned to * @link: Original object being linked (before conversion) * @newtype: New type that link needs to be converted to * @user_data: User defined data supplied to the converter instance * * This function type allows for object link interception by the user of * an #IpatchConverter instance. It is called by conversion processes which * create objects linking other external objects which need to be converted * also. If this function returns %NULL then the @link will be converted * by the converter process and the user notified with the * #IpatchConverterLinkNotifyFunc. An example usage of this feature is * the #IpatchPaste system, which does object conversions and substitutes * already converted objects (a conversion pool). * * Returns: Existing converted item satisfying @link and @newtype, or %NULL * otherwise. */ typedef GObject *(*IpatchConverterLinkLookupFunc) (IpatchConverter *converter, GObject *item, GObject *link, GType newtype, gpointer user_data); /** * IpatchConverterLinkNotifyFunc: * @converter: Converter instance * @orig: Original link item which was converted * @conv: New converted link item * @newtype: New type that link was converted to (same as passed to * #IpatchConverterLinkLookupFunc). * @user_data: User defined data supplied to the converter instance * * This function type allows for object link interception by the user of * an #IpatchConverter instance. It is called by conversion processes which * create objects linking other external objects which need to be converted * also. For each link object which needs to be converted * #IpatchConverterLinkLookupFunc is called first, if it returns %NULL then * this function will be called with the newly converted link object. */ typedef void (*IpatchConverterLinkNotifyFunc) (IpatchConverter *converter, GObject *orig, GObject *conv, GType newtype, gpointer user_data); /* conversion instance */ struct _IpatchConverter { GObject parent_instance; /* derived from GObject */ int flags; /* IpatchConverterFlags */ GList *inputs; /* list of input GObjects to convert */ GList *outputs; /* list of new converted output GObjects */ /* callbacks for object link interception */ IpatchConverterLinkLookupFunc *link_lookup; IpatchConverterLinkNotifyFunc *link_notify; GDestroyNotify notify_func; // Callback for when the above callbacks are removed/replaced gpointer user_data; // User data passed to notify_func (not the other callbacks) float progress; /* 0.0 - 1.0 progress property */ /* conversion ratings (0.0 - 1.0 = worst - best). For container objects ratings can be done individually on the children, then min_rate/max_rate will be useful */ float min_rate; /* minimum rating amongst all items */ float max_rate; /* maximum rating amongst all items */ float avg_rate; /* average rating for all items */ float sum_rate; /* sum of all ratings (to calculate avg) */ int item_count; /* count of children items being rated */ gboolean rate_items; /* set to TRUE to log a rating for each child item */ /* conversion log */ GList *log; /* LogEntry list (defined in IpatchConverter.c, prepended) */ }; /* conversion class */ struct _IpatchConverterClass { GObjectClass parent_class; /* methods */ gboolean(*verify)(IpatchConverter *converter, char **failmsg); void (*init)(IpatchConverter *converter); gboolean(*convert)(IpatchConverter *converter, GError **err); char *(*notes)(IpatchConverter *converter); }; /* type for log entries */ typedef enum { IPATCH_CONVERTER_LOG_RATING, /* log a rating update */ IPATCH_CONVERTER_LOG_INFO, /* informational only */ IPATCH_CONVERTER_LOG_WARN, /* warning */ IPATCH_CONVERTER_LOG_CRITICAL, /* critical (but non fatal) message */ IPATCH_CONVERTER_LOG_FATAL /* fatal error */ } IpatchConverterLogType; /* mask for type field (IpatchConverterLogType) */ #define IPATCH_CONVERTER_LOG_TYPE_MASK 0x0F /* flag for IpatchConverterLog->type to indicate allocated message string */ #define IPATCH_CONVERTER_LOG_MSG_ALLOC 0x80 #define IPATCH_CONVERTER_INPUT(converter) \ (converter->inputs ? G_OBJECT (converter->inputs->data) : (GObject *)NULL) #define IPATCH_CONVERTER_OUTPUT(converter) \ (converter->outputs ? G_OBJECT (converter->outputs->data) : (GObject *)NULL) /* enum used for src_count and dest_count fields in class */ typedef enum { IPATCH_CONVERTER_COUNT_ONE_OR_MORE = -1, /* 1 or more objects */ IPATCH_CONVERTER_COUNT_ZERO_OR_MORE = -2 /* 0 or more objects */ } IpatchConverterCount; /** * IpatchConverterFlags: * @IPATCH_CONVERTER_SRC_DERIVED: Match source derived types also (type descendants of src_type) * @IPATCH_CONVERTER_DEST_DERIVED: Match destination derived types also (type descendants of dest_type) * * Flags for ipatch_register_converter_map() * * Since: 1.1.0 */ typedef enum { IPATCH_CONVERTER_SRC_DERIVED = 1 << 8, IPATCH_CONVERTER_DEST_DERIVED = 1 << 9, } IpatchConverterFlags; /* priority levels for converter mappings */ typedef enum { /* 0 value is an alias for IPATCH_CONVERTER_PRIORITY_DEFAULT */ IPATCH_CONVERTER_PRIORITY_LOWEST = 1, IPATCH_CONVERTER_PRIORITY_LOW = 25, IPATCH_CONVERTER_PRIORITY_DEFAULT = 50, IPATCH_CONVERTER_PRIORITY_HIGH = 75, IPATCH_CONVERTER_PRIORITY_HIGHEST = 100 } IpatchConverterPriority; /** * IpatchConverterInfo: * @conv_type: Conversion handler type * @src_type: Source type of conversion handler * @src_match: Furthest source parent type to match (0 = exact match) * @dest_type: Destination type of conversion handler * @dest_match: Furthest dest parent type to match (0 = exact match) * @flags: #IpatchConverterFlags | priority (#IpatchConverterPriority value or other integer value) * @src_count: Required source item count or IpatchConverterCount * @dest_count: Required destination item count or IpatchConverterCount * * Registered object converter information. */ struct _IpatchConverterInfo { GType conv_type; GType src_type; GType src_match; GType dest_type; GType dest_match; guint8 flags; guint8 priority; gint8 src_count; gint8 dest_count; }; gboolean ipatch_convert_objects(GObject *input, GObject *output, GError **err); GObject *ipatch_convert_object_to_type(GObject *object, GType type, GError **err); IpatchList *ipatch_convert_object_to_type_multi(GObject *object, GType type, GError **err); GList *ipatch_convert_object_to_type_multi_list(GObject *object, GType type, GError **err); IpatchList *ipatch_convert_object_to_type_multi_set(GObject *object, GType type, GError **err, const char *first_property_name, ...); GList *ipatch_convert_object_to_type_multi_set_vlist(GObject *object, GType type, GError **err, const char *first_property_name, va_list args); IpatchConverter *ipatch_create_converter(GType src_type, GType dest_type); IpatchConverter *ipatch_create_converter_for_objects(GObject *input, GObject *output, GError **err); IpatchConverter *ipatch_create_converter_for_object_to_type(GObject *object, GType dest_type, GError **err); void ipatch_register_converter_map(GType conv_type, guint8 flags, guint8 priority, GType src_type, GType src_match, gint8 src_count, GType dest_type, GType dest_match, gint8 dest_count); GType ipatch_find_converter(GType src_type, GType dest_type); GType *ipatch_find_converters(GType src_type, GType dest_type, guint flags); const IpatchConverterInfo *ipatch_lookup_converter_info(GType conv_type, GType src_type, GType dest_type); const IpatchConverterInfo *ipatch_get_converter_info(GType conv_type); GType ipatch_converter_get_type(void); void ipatch_converter_add_input(IpatchConverter *converter, GObject *object); void ipatch_converter_add_output(IpatchConverter *converter, GObject *object); void ipatch_converter_add_inputs(IpatchConverter *converter, GList *objects); void ipatch_converter_add_outputs(IpatchConverter *converter, GList *objects); GObject *ipatch_converter_get_input(IpatchConverter *converter); GObject *ipatch_converter_get_output(IpatchConverter *converter); IpatchList *ipatch_converter_get_inputs(IpatchConverter *converter); GList *ipatch_converter_get_inputs_list(IpatchConverter *converter); IpatchList *ipatch_converter_get_outputs(IpatchConverter *converter); GList *ipatch_converter_get_outputs_list(IpatchConverter *converter); gboolean ipatch_converter_verify(IpatchConverter *converter, char **failmsg); void ipatch_converter_init(IpatchConverter *converter); gboolean ipatch_converter_convert(IpatchConverter *converter, GError **err); void ipatch_converter_reset(IpatchConverter *converter); char *ipatch_converter_get_notes(IpatchConverter *converter); void ipatch_converter_log(IpatchConverter *converter, GObject *item, int type, char *msg); void ipatch_converter_log_printf(IpatchConverter *converter, GObject *item, int type, const char *fmt, ...); gboolean ipatch_converter_log_next(IpatchConverter *converter, gpointer *pos, GObject **item, int *type, char **msg); void ipatch_converter_set_link_funcs(IpatchConverter *converter, IpatchConverterLinkLookupFunc *link_lookup, IpatchConverterLinkNotifyFunc *link_notify); void ipatch_converter_set_link_funcs_full(IpatchConverter *converter, IpatchConverterLinkLookupFunc *link_lookup, IpatchConverterLinkNotifyFunc *link_notify, GDestroyNotify notify_func, gpointer user_data); #endif libinstpatch-1.1.6/libinstpatch/IpatchConverterSF2VoiceCache.c000066400000000000000000000067101400263525300243640ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchConverterSF2VoiceCache * @short_description: Base object type used for #IpatchSF2Voice cache converters * @see_also: * @stability: Stable * * Defines a base type which other #IpatchSF2Voice converter types are derived * from. Defines some properties like "solo-item" for solo-ing a sub * component of an instrument. */ #include "IpatchConverterSF2VoiceCache.h" #include "i18n.h" enum { PROP_0, PROP_SOLO_ITEM }; static void ipatch_converter_sf2_voice_cache_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_converter_sf2_voice_cache_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); G_DEFINE_ABSTRACT_TYPE(IpatchConverterSF2VoiceCache, ipatch_converter_sf2_voice_cache, IPATCH_TYPE_CONVERTER) static void ipatch_converter_sf2_voice_cache_class_init(IpatchConverterSF2VoiceCacheClass *klass) { GObjectClass *obj_class = (GObjectClass *)klass; obj_class->set_property = ipatch_converter_sf2_voice_cache_set_property; obj_class->get_property = ipatch_converter_sf2_voice_cache_get_property; g_object_class_install_property(obj_class, PROP_SOLO_ITEM, g_param_spec_object("solo-item", _("Solo item"), _("Solo item"), IPATCH_TYPE_ITEM, G_PARAM_READWRITE)); } static void ipatch_converter_sf2_voice_cache_init(IpatchConverterSF2VoiceCache *converter) { } static void ipatch_converter_sf2_voice_cache_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchConverterSF2VoiceCache *converter = IPATCH_CONVERTER_SF2_VOICE_CACHE(object); /* No lock needed, since converters aren't multi-threaded */ switch(property_id) { case PROP_SOLO_ITEM: if(converter->solo_item) { g_object_unref(converter->solo_item); } converter->solo_item = g_value_get_object(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_converter_sf2_voice_cache_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchConverterSF2VoiceCache *converter = IPATCH_CONVERTER_SF2_VOICE_CACHE(object); /* No lock needed, since converters aren't multi-threaded */ switch(property_id) { case PROP_SOLO_ITEM: g_value_set_object(value, converter->solo_item); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } libinstpatch-1.1.6/libinstpatch/IpatchConverterSF2VoiceCache.h000066400000000000000000000041601400263525300243660ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_CONVERTER_SF2_VOICE_CACHE_H__ #define __IPATCH_CONVERTER_SF2_VOICE_CACHE_H__ typedef struct _IpatchConverterSF2VoiceCache IpatchConverterSF2VoiceCache; typedef struct _IpatchConverterSF2VoiceCacheClass IpatchConverterSF2VoiceCacheClass; #include #include #include #include #define IPATCH_TYPE_CONVERTER_SF2_VOICE_CACHE \ (ipatch_converter_sf2_voice_cache_get_type ()) #define IPATCH_CONVERTER_SF2_VOICE_CACHE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_CONVERTER_SF2_VOICE_CACHE, \ IpatchConverterSF2VoiceCache)) #define IPATCH_CONVERTER_SF2_VOICE_CACHE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_CONVERTER_SF2_VOICE_CACHE, \ IpatchSF2VoiceCacheClass)) #define IPATCH_IS_CONVERTER_SF2_VOICE_CACHE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_CONVERTER_SF2_VOICE_CACHE)) #define IPATCH_IS_CONVERTER_SF2_VOICE_CACHE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_CONVERTER_SF2_VOICE_CACHE)) struct _IpatchConverterSF2VoiceCache { IpatchConverter parent_instance; IpatchItem *solo_item; }; struct _IpatchConverterSF2VoiceCacheClass { IpatchConverterClass parent_class; }; GType ipatch_converter_sf2_voice_cache_get_type(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchConverter_priv.h000066400000000000000000000052441400263525300232050ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /* * IpatchConverter_priv.h - Helper macros for defining converters */ #ifndef __IPATCH_CONVERTER_PRIV_H__ #define __IPATCH_CONVERTER_PRIV_H__ #define CONVERTER_GET_TYPE(type_under, TypeCase) \ GType ipatch_converter_ ## type_under ## _get_type (void) \ { \ static GType obj_type = 0; \ \ if (!obj_type) { \ static const GTypeInfo obj_info = { \ sizeof (IpatchConverter ## TypeCase ## Class), NULL, NULL, \ (GClassInitFunc) _ ## type_under ## _class_init, NULL, NULL, \ sizeof (IpatchConverter ## TypeCase), 0, \ (GInstanceInitFunc) NULL, \ }; \ \ obj_type = g_type_register_static (IPATCH_TYPE_CONVERTER, \ "IpatchConverter" #TypeCase, \ &obj_info, 0); \ } \ \ return (obj_type); \ } #define CONVERTER_CLASS_INIT(type_under) \ static void _ ## type_under ## _class_init (IpatchConverterClass *klass) \ { \ klass->verify = NULL; \ klass->notes = NULL; \ klass->convert = _ ## type_under ## _convert; \ } #define CONVERTER_CLASS_INIT_NOTES(type_under) \ static void _ ## type_under ## _class_init (IpatchConverterClass *klass) \ { \ klass->verify = NULL; \ klass->notes = _ ## type_under ## _notes; \ klass->convert = _ ## type_under ## _convert; \ } #define CONVERTER_SF2_VOICE_CACHE_GET_TYPE(type_under, TypeCase) \ GType ipatch_converter_ ## type_under ## _get_type (void) \ { \ static GType obj_type = 0; \ \ if (!obj_type) { \ static const GTypeInfo obj_info = { \ sizeof (IpatchConverter ## TypeCase ## Class), NULL, NULL, \ (GClassInitFunc) _ ## type_under ## _class_init, NULL, NULL, \ sizeof (IpatchConverter ## TypeCase), 0, \ (GInstanceInitFunc) NULL, \ }; \ \ obj_type = g_type_register_static (IPATCH_TYPE_CONVERTER_SF2_VOICE_CACHE, \ "IpatchConverter" #TypeCase, \ &obj_info, 0); \ } \ \ return (obj_type); \ } #endif libinstpatch-1.1.6/libinstpatch/IpatchDLS2.c000066400000000000000000000656351400263525300207070ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchDLS2 * @short_description: DLS version 2 instrument file object * @see_also: * @stability: Stable * * Object type for DLS version 2 format instruments. */ #include #include #include #include #include "IpatchDLS2.h" #include "IpatchDLSFile.h" #include "IpatchDLS2Info.h" #include "IpatchDLS2Region.h" #include "IpatchTypeProp.h" #include "IpatchVirtualContainer_types.h" #include "version.h" #include "ipatch_priv.h" /* non-INFO properties (INFO properties use their FOURCC int values) */ enum { PROP_0, PROP_VERSION }; static void ipatch_dls2_class_init(IpatchDLS2Class *klass); static void ipatch_dls2_init(IpatchDLS2 *dls); static void ipatch_dls2_finalize(GObject *gobject); static void ipatch_dls2_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_dls2_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_dls2_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static const GType *ipatch_dls2_container_child_types(void); static const GType *ipatch_dls2_container_virtual_types(void); static gboolean ipatch_dls2_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type); static void ipatch_dls2_container_make_unique(IpatchContainer *container, IpatchItem *item); static void ipatch_dls2_base_find_unused_locale(IpatchBase *base, int *bank, int *program, const IpatchItem *exclude, gboolean percussion); static int locale_gcompare_func(gconstpointer a, gconstpointer b); static IpatchItem * ipatch_dls2_base_find_item_by_locale(IpatchBase *base, int bank, int program); static gpointer parent_class = NULL; static GType dls2_child_types[3] = { 0 }; static GType dls2_virt_types[4] = { 0 }; /* SoundFont item type creation function */ GType ipatch_dls2_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchDLS2Class), NULL, NULL, (GClassInitFunc)ipatch_dls2_class_init, NULL, NULL, sizeof(IpatchDLS2), 0, (GInstanceInitFunc)ipatch_dls2_init, }; item_type = g_type_register_static(IPATCH_TYPE_BASE, "IpatchDLS2", &item_info, 0); } return (item_type); } static void ipatch_dls2_class_init(IpatchDLS2Class *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass); IpatchBaseClass *base_class = IPATCH_BASE_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->finalize = ipatch_dls2_finalize; obj_class->get_property = ipatch_dls2_get_property; /* we use the IpatchItem item_set_property method */ item_class->item_set_property = ipatch_dls2_set_property; item_class->copy = ipatch_dls2_item_copy; container_class->child_types = ipatch_dls2_container_child_types; container_class->virtual_types = ipatch_dls2_container_virtual_types; container_class->init_iter = ipatch_dls2_container_init_iter; container_class->make_unique = ipatch_dls2_container_make_unique; base_class->find_unused_locale = ipatch_dls2_base_find_unused_locale; base_class->find_item_by_locale = ipatch_dls2_base_find_item_by_locale; /* non INFO properties */ g_object_class_override_property(obj_class, IPATCH_DLS2_NAME, "title"); g_object_class_install_property(obj_class, PROP_VERSION, g_param_spec_string("version", _("Version"), _("File version \"n.n.n.n\""), NULL, G_PARAM_READWRITE)); ipatch_dls2_info_install_class_properties(obj_class); dls2_child_types[0] = IPATCH_TYPE_DLS2_INST; dls2_child_types[1] = IPATCH_TYPE_DLS2_SAMPLE; dls2_virt_types[0] = IPATCH_TYPE_VIRTUAL_DLS2_MELODIC; dls2_virt_types[1] = IPATCH_TYPE_VIRTUAL_DLS2_PERCUSSION; dls2_virt_types[2] = IPATCH_TYPE_VIRTUAL_DLS2_SAMPLES; } static void ipatch_dls2_init(IpatchDLS2 *dls) { g_object_set(dls, "name", _(IPATCH_BASE_DEFAULT_NAME), "software", "libInstPatch v" IPATCH_VERSION, NULL); ipatch_item_clear_flags(IPATCH_ITEM(dls), IPATCH_BASE_CHANGED); } /* function called when SoundFont is being destroyed */ static void ipatch_dls2_finalize(GObject *gobject) { IpatchDLS2 *dls = IPATCH_DLS2(gobject); IPATCH_ITEM_WLOCK(dls); ipatch_dls2_info_free(dls->info); dls->info = NULL; g_free(dls->dlid); dls->dlid = NULL; IPATCH_ITEM_WUNLOCK(dls); if(G_OBJECT_CLASS(parent_class)->finalize) { G_OBJECT_CLASS(parent_class)->finalize(gobject); } } static void ipatch_dls2_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchDLS2 *dls = IPATCH_DLS2(object); gboolean retval; if(property_id == PROP_VERSION) { const char *verstr; guint16 msu, msl, lsu, lsl; verstr = g_value_get_string(value); if(verstr && sscanf(verstr, "%hu.%hu.%hu.%hu", &msu, &msl, &lsu, &lsl) != 4) { g_warning("Version property parse error"); return; } IPATCH_ITEM_WLOCK(dls); if(verstr) { ipatch_item_set_flags(dls, IPATCH_DLS2_VERSION_SET); dls->ms_version = (guint32)msu << 16 || msl; dls->ls_version = (guint32)lsu << 16 || lsl; } else { ipatch_item_clear_flags(dls, IPATCH_DLS2_VERSION_SET); } IPATCH_ITEM_WUNLOCK(dls); } else { IPATCH_ITEM_WLOCK(dls); retval = ipatch_dls2_info_set_property(&dls->info, property_id, value); IPATCH_ITEM_WUNLOCK(dls); /* check of "title" property needs to be notified, values do not need to be sent, since its a read only property */ if(property_id == IPATCH_DLS2_NAME) ipatch_item_prop_notify((IpatchItem *)dls, ipatch_item_pspec_title, value, NULL); if(!retval) { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } } static void ipatch_dls2_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchDLS2 *dls; gboolean retval; char *s; g_return_if_fail(IPATCH_IS_DLS2(object)); dls = IPATCH_DLS2(object); if(property_id == PROP_VERSION) { gboolean version_set; guint32 ms, ls; IPATCH_ITEM_RLOCK(dls); version_set = (ipatch_item_get_flags(dls) & IPATCH_DLS2_VERSION_SET) > 0; ms = dls->ms_version; ls = dls->ls_version; IPATCH_ITEM_RUNLOCK(dls); if(version_set) { s = g_strdup_printf("%u.%u.%u.%u", ms >> 16, ms & 0xFFFF, ls >> 16, ls & 0xFFFF); g_value_take_string(value, s); } else { g_value_set_string(value, NULL); } } else /* INFO property or invalid */ { IPATCH_ITEM_RLOCK(dls); retval = ipatch_dls2_info_get_property(dls->info, property_id, value); IPATCH_ITEM_RUNLOCK(dls); if(!retval) { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } } static void ipatch_dls2_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchDLS2 *src_dls, *dest_dls; IpatchItem *newitem; GHashTable *repl_samples; GSList *p; src_dls = IPATCH_DLS2(src); dest_dls = IPATCH_DLS2(dest); /* create item replacement hash */ repl_samples = g_hash_table_new(NULL, NULL); IPATCH_ITEM_RLOCK(src_dls); dest_dls->ms_version = src_dls->ms_version; dest_dls->ls_version = src_dls->ls_version; if(ipatch_item_get_flags(src_dls) & IPATCH_DLS2_VERSION_SET) { ipatch_item_set_flags(dest_dls, IPATCH_DLS2_VERSION_SET); } if(IPATCH_BASE(src_dls)->file) { ipatch_base_set_file(IPATCH_BASE(dest_dls), IPATCH_BASE(src_dls)->file); } dest_dls->info = ipatch_dls2_info_duplicate(src_dls->info); if(src_dls->dlid) { dest_dls->dlid = g_memdup(src_dls->dlid, IPATCH_DLS_DLID_SIZE); } p = src_dls->samples; while(p) /* duplicate samples */ { newitem = ipatch_item_duplicate((IpatchItem *)(p->data)); g_return_if_fail(newitem != NULL); dest_dls->samples = g_slist_prepend(dest_dls->samples, newitem); ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_dls)); /* add to sample pointer replacement hash */ g_hash_table_insert(repl_samples, p->data, newitem); /* FIXME - phase linked groups? */ p = g_slist_next(p); } p = src_dls->insts; while(p) /* duplicate instruments */ { newitem = ipatch_item_duplicate_replace((IpatchItem *)(p->data), repl_samples); g_return_if_fail(newitem != NULL); dest_dls->insts = g_slist_prepend(dest_dls->insts, newitem); ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_dls)); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(src_dls); dest_dls->insts = g_slist_reverse(dest_dls->insts); dest_dls->samples = g_slist_reverse(dest_dls->samples); g_hash_table_destroy(repl_samples); } static const GType * ipatch_dls2_container_child_types(void) { return (dls2_child_types); } static const GType * ipatch_dls2_container_virtual_types(void) { return (dls2_virt_types); } /* container is locked by caller */ static gboolean ipatch_dls2_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type) { IpatchDLS2 *dls = IPATCH_DLS2(container); if(g_type_is_a(type, IPATCH_TYPE_DLS2_INST)) { ipatch_iter_GSList_init(iter, &dls->insts); } else if(g_type_is_a(type, IPATCH_TYPE_DLS2_SAMPLE)) { ipatch_iter_GSList_init(iter, &dls->samples); } else { g_critical("Invalid child type '%s' for parent of type '%s'", g_type_name(type), g_type_name(G_OBJECT_TYPE(container))); return (FALSE); } return (TRUE); } static void ipatch_dls2_container_make_unique(IpatchContainer *container, IpatchItem *item) { IpatchDLS2 *dls = IPATCH_DLS2(container); gboolean perc; char *name, *newname; IPATCH_ITEM_WLOCK(dls); if(IPATCH_IS_DLS2_INST(item)) { int bank, newbank, program, newprogram; ipatch_dls2_inst_get_midi_locale(IPATCH_DLS2_INST(item), &bank, &program); newbank = bank; newprogram = program; perc = (ipatch_item_get_flags(item) & IPATCH_DLS2_INST_PERCUSSION) != 0; ipatch_base_find_unused_midi_locale(IPATCH_BASE(dls), &newbank, &newprogram, item, perc); if(bank != newbank || program != newprogram) ipatch_dls2_inst_set_midi_locale(IPATCH_DLS2_INST(item), newbank, newprogram); } else if(!IPATCH_IS_DLS2_SAMPLE(item)) { g_critical("Invalid child type '%s' for IpatchDLS2 object", g_type_name(G_TYPE_FROM_INSTANCE(item))); IPATCH_ITEM_WUNLOCK(dls); return; } g_object_get(item, "name", &name, NULL); newname = ipatch_dls2_make_unique_name(dls, G_TYPE_FROM_INSTANCE(item), name, NULL); if(!name || strcmp(name, newname) != 0) { g_object_set(item, "name", newname, NULL); } IPATCH_ITEM_WUNLOCK(dls); g_free(name); g_free(newname); } /* base method to find an unused MIDI bank:program locale */ static void ipatch_dls2_base_find_unused_locale(IpatchBase *base, int *bank, int *program, const IpatchItem *exclude, gboolean percussion) { IpatchDLS2 *dls = IPATCH_DLS2(base); GSList *locale_list = NULL; IpatchDLS2Inst *inst; GSList *p; guint32 b, n; /* Stores current bank and preset number */ guint32 lbank, lprogram; /* fill array with bank and preset numbers */ IPATCH_ITEM_RLOCK(dls); p = dls->insts; while(p) { inst = (IpatchDLS2Inst *)(p->data); /* only add to locale list if not the exclude item */ if((gpointer)inst != (gpointer)exclude) locale_list = g_slist_prepend(locale_list, GUINT_TO_POINTER ((inst->bank << 16) | inst->program)); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(dls); if(!locale_list) { *program = 0; return; } locale_list = g_slist_sort(locale_list, (GCompareFunc)locale_gcompare_func); b = (guint32) * bank; n = (guint32) * program; /* loop through sorted list of bank:programs */ p = locale_list; while(p) { lprogram = GPOINTER_TO_UINT(p->data); lbank = lprogram >> 16; lprogram &= 0xFFFF; if(lbank > b || (lbank == b && lprogram > n)) { break; } if(lbank >= b) { if(++n > 127) { n = 0; b++; } } p = g_slist_delete_link(p, p); /* delete and advance */ } *bank = b; *program = n; if(p) { g_slist_free(p); /* free remainder of list */ } } /* function used to do a temporary sort on preset list for ipatch_dls2_base_find_unused_locale */ static int locale_gcompare_func(gconstpointer a, gconstpointer b) { return (GPOINTER_TO_UINT(a) - GPOINTER_TO_UINT(b)); } static IpatchItem * ipatch_dls2_base_find_item_by_locale(IpatchBase *base, int bank, int program) { IpatchDLS2Inst *inst; inst = ipatch_dls2_find_inst(IPATCH_DLS2(base), NULL, bank, program, NULL); return ((IpatchItem *)inst); } /** * ipatch_dls2_new: * * Create a new DLS base object. * * Returns: New DLS base object with a reference count of 1. Caller * owns the reference and removing it will destroy the item. */ IpatchDLS2 * ipatch_dls2_new(void) { return (IPATCH_DLS2(g_object_new(IPATCH_TYPE_DLS2, NULL))); } /** * ipatch_dls2_set_file: * @dls: DLS to set file object of * @file: File object to use with the DLS object * * Sets the file object of a DLS object. DLS files are kept open * for sample data that references the file. This function sets a * DLS object's authoritive file. A convenience function, as * ipatch_base_set_file() does the same thing (albeit without more specific * type casting). */ void ipatch_dls2_set_file(IpatchDLS2 *dls, IpatchDLSFile *file) { g_return_if_fail(IPATCH_IS_DLS2(dls)); g_return_if_fail(IPATCH_IS_DLS_FILE(file)); ipatch_base_set_file(IPATCH_BASE(dls), IPATCH_FILE(file)); } /** * ipatch_dls2_get_file: * @dls: DLS object to get file object of * * Gets the file object of a DLS. The returned DLS file object's * reference count has been incremented. The caller owns the reference and is * responsible for removing it with g_object_unref. * A convenience function as ipatch_base_get_file() does the same thing * (albeit without more specific type casting). * * Returns: (transfer full): The DLS file object or %NULL if @dls is not open. Remember * to unref the file object with g_object_unref() when done with it. */ IpatchDLSFile * ipatch_dls2_get_file(IpatchDLS2 *dls) { IpatchFile *file; g_return_val_if_fail(IPATCH_IS_DLS2(dls), NULL); file = ipatch_base_get_file(IPATCH_BASE(dls)); if(file) { return (IPATCH_DLS_FILE(file)); } else { return (NULL); } } /** * ipatch_dls2_get_info: * @dls: DLS to get info from * @fourcc: FOURCC integer id of INFO to get * * Get a DLS info string by FOURCC integer ID (integer representation of * a 4 character RIFF chunk ID, see #IpatchRiff). * * Returns: New allocated info string value or %NULL if no info with the * given @fourcc ID. String should be freed when finished with it. */ char * ipatch_dls2_get_info(IpatchDLS2 *dls, guint32 fourcc) { char *val; g_return_val_if_fail(IPATCH_IS_DLS2(dls), NULL); IPATCH_ITEM_RLOCK(dls); val = ipatch_dls2_info_get(dls->info, fourcc); IPATCH_ITEM_RUNLOCK(dls); return (val); } /** * ipatch_dls2_set_info: * @dls: DLS to set info of * @fourcc: FOURCC integer ID of INFO to set * @val: (nullable): Value to set info to or %NULL to unset (clear) info. * * Sets an INFO value in a DLS object. * * Emits changed signal on DLS object. */ void ipatch_dls2_set_info(IpatchDLS2 *dls, guint32 fourcc, const char *val) { GValue newval = { 0 }, oldval = { 0 }; g_return_if_fail(IPATCH_IS_DLS2(dls)); g_value_init(&newval, G_TYPE_STRING); g_value_set_static_string(&newval, val); g_value_init(&oldval, G_TYPE_STRING); g_value_take_string(&oldval, ipatch_dls2_get_info(dls, fourcc)); IPATCH_ITEM_WLOCK(dls); ipatch_dls2_info_set(&dls->info, fourcc, val); IPATCH_ITEM_WUNLOCK(dls); ipatch_dls2_info_notify((IpatchItem *)dls, fourcc, &newval, &oldval); /* need to do title notify? */ if(fourcc == IPATCH_DLS2_NAME) ipatch_item_prop_notify((IpatchItem *)dls, ipatch_item_pspec_title, &newval, &oldval); g_value_unset(&oldval); g_value_unset(&newval); } /** * ipatch_dls2_make_unique_name: * @dls: DLS object * @child_type: A child type of @dls to search for a unique name in * @name: (nullable): An initial name to use or NULL * @exclude: (nullable): An item to exclude from search or NULL * * Generates a unique name for the given @child_type in @dls. The @name * parameter is used as a base and is modified, by appending a number, to * make it unique (if necessary). The @exclude parameter is used to exclude * an existing @dls child item from the search. * * MT-Note: To ensure that an item is actually unique before being * added to a DLS object, ipatch_container_add_unique() should be * used. * * Returns: A new unique name which should be freed when finished with it. */ char * ipatch_dls2_make_unique_name(IpatchDLS2 *dls, GType child_type, const char *name, const IpatchItem *exclude) { GSList **list, *p; char *curname, *numptr; const char *temp; guint count = 2, info_ofs, len; g_return_val_if_fail(IPATCH_IS_DLS2(dls), NULL); if(g_type_is_a(child_type, IPATCH_TYPE_DLS2_INST)) { list = &dls->insts; info_ofs = G_STRUCT_OFFSET(IpatchDLS2Inst, info); if(!name || !*name) { name = _("New Instrument"); } } else if(g_type_is_a(child_type, IPATCH_TYPE_DLS2_SAMPLE)) { list = &dls->samples; info_ofs = G_STRUCT_OFFSET(IpatchDLS2Sample, info); if(!name || !*name) { name = _("New Sample"); } } else { g_critical("Invalid child type '%s' of parent type '%s'", g_type_name(child_type), g_type_name(G_OBJECT_TYPE(dls))); return (NULL); } len = strlen(name); /* allocate string size + 10 chars for number + zero termination */ curname = g_malloc0(len + 10 + 1); strcpy(curname, name); /* copy name */ numptr = curname + len; /* pointer to end of name to concat number */ IPATCH_ITEM_RLOCK(dls); p = *list; while(p) /* check for duplicate */ { IPATCH_ITEM_RLOCK(p->data); /* MT - Recursive LOCK */ if(p->data != exclude) { temp = ipatch_dls2_info_peek (G_STRUCT_MEMBER(IpatchDLS2Info *, p->data, info_ofs), IPATCH_DLS2_NAME); if(temp && strcmp(temp, curname) == 0) { /* duplicate name */ IPATCH_ITEM_RUNLOCK(p->data); sprintf(numptr, "%u", count++); p = *list; /* start over */ continue; } } IPATCH_ITEM_RUNLOCK(p->data); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(dls); curname = g_realloc(curname, strlen(curname) + 1); return (curname); } /** * ipatch_dls2_find_inst: * @dls: DLS object to search in * @name: (nullable): Name of instrument to find or %NULL to match any name * @bank: MIDI bank number of instrument to search for or -1 to not search by * MIDI bank:program numbers * @program: MIDI program number of instrument to search for, only used * if @bank is 0-128 * @exclude: (nullable): A instrument to exclude from the search or %NULL * * Find a instrument by name or bank:program MIDI numbers. If instrument @name * and @bank:@program are specified then match for either condition. * If a instrument is found its reference count is incremented before it * is returned. The caller is responsible for removing the reference * with g_object_unref() when finished with it. * * Returns: (transfer full): The matching instrument or %NULL if not found. Remember to unref * the item when finished with it. */ IpatchDLS2Inst * ipatch_dls2_find_inst(IpatchDLS2 *dls, const char *name, int bank, int program, const IpatchDLS2Inst *exclude) { IpatchDLS2Inst *inst; gboolean bynum = FALSE; const char *temp = NULL; GSList *p; g_return_val_if_fail(IPATCH_IS_DLS2(dls), NULL); /* if bank and program are valid, then search by number */ if(bank >= 0 && program >= 0 && program < 128) { bynum = TRUE; } IPATCH_ITEM_RLOCK(dls); p = dls->insts; while(p) { inst = (IpatchDLS2Inst *)(p->data); IPATCH_ITEM_RLOCK(inst); /* MT - Recursive LOCK */ if(inst != exclude && ((bynum && inst->bank == bank && inst->program == program) || (name && (temp = ipatch_dls2_info_peek (inst->info, IPATCH_DLS2_NAME)) && strcmp(temp, name) == 0))) { g_object_ref(inst); IPATCH_ITEM_RUNLOCK(inst); IPATCH_ITEM_RUNLOCK(dls); return (inst); } IPATCH_ITEM_RUNLOCK(inst); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(dls); return (NULL); } /** * ipatch_dls2_find_sample: * @dls: DLS object to search in * @name: Name of sample to find * @exclude: (nullable): A sample to exclude from the search or %NULL * * Find a sample by @name in a SoundFont. If a sample is found its * reference count is incremented before it is returned. The caller * is responsible for removing the reference with g_object_unref() * when finished with it. * * Returns: (transfer full): The matching sample or %NULL if not found. Remember to unref * the item when finished with it. */ IpatchDLS2Sample * ipatch_dls2_find_sample(IpatchDLS2 *dls, const char *name, const IpatchDLS2Sample *exclude) { IpatchDLS2Sample *sample; const char *temp = NULL; GSList *p; g_return_val_if_fail(IPATCH_IS_DLS2(dls), NULL); g_return_val_if_fail(name != NULL, NULL); IPATCH_ITEM_RLOCK(dls); p = dls->samples; while(p) { sample = (IpatchDLS2Sample *)(p->data); IPATCH_ITEM_RLOCK(sample); /* MT - Recursive LOCK */ if(sample != exclude && (temp = ipatch_dls2_info_peek (sample->info, IPATCH_DLS2_NAME)) && strcmp(temp, name) == 0) { g_object_ref(sample); IPATCH_ITEM_RUNLOCK(sample); IPATCH_ITEM_RUNLOCK(dls); return (sample); } IPATCH_ITEM_RUNLOCK(sample); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(dls); return (NULL); } /** * ipatch_dls2_get_region_references: * @sample: Sample to locate referencing regions of. * * Get list of regions referencing an #IpatchDLS2Sample. * * Returns: (transfer full): New item list containing #IpatchDLS2Region objects * that refer to @sample. The returned list has a reference count of 1 which * the caller owns, unreference it when done. */ IpatchList * ipatch_dls2_get_region_references(IpatchDLS2Sample *sample) { IpatchDLS2 *dls; IpatchDLS2Region *region; IpatchList *refitems; IpatchIter iter, region_iter; IpatchDLS2Inst *inst; IpatchItem *pitem; gboolean success; g_return_val_if_fail(IPATCH_IS_DLS2_SAMPLE(sample), NULL); pitem = ipatch_item_get_parent(IPATCH_ITEM(sample)); g_return_val_if_fail(IPATCH_IS_DLS2(pitem), NULL); dls = IPATCH_DLS2(pitem); refitems = ipatch_list_new(); IPATCH_ITEM_RLOCK(dls); success = ipatch_container_init_iter((IpatchContainer *)dls, &iter, IPATCH_TYPE_DLS2_INST); g_return_val_if_fail(success != FALSE, NULL); inst = ipatch_dls2_inst_first(&iter); while(inst) /* loop over instruments */ { IPATCH_ITEM_RLOCK(inst); /* ## embedded lock */ success = ipatch_container_init_iter((IpatchContainer *)dls, ®ion_iter, IPATCH_TYPE_DLS2_INST); g_return_val_if_fail(success != FALSE, NULL); region = ipatch_dls2_region_first(®ion_iter); while(region) /* loop over regions */ { if(ipatch_dls2_region_peek_sample(region) == sample) { g_object_ref(region); /* ++ ref region for new iterator */ refitems->items = g_list_prepend(refitems->items, region); } region = ipatch_dls2_region_next(®ion_iter); } IPATCH_ITEM_RUNLOCK(inst); inst = ipatch_dls2_inst_next(&iter); } IPATCH_ITEM_RUNLOCK(dls); return (refitems); } libinstpatch-1.1.6/libinstpatch/IpatchDLS2.h000066400000000000000000000077371400263525300207130ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_DLS2_H__ #define __IPATCH_DLS2_H__ #include #include /* forward type declarations */ typedef struct _IpatchDLS2 IpatchDLS2; typedef struct _IpatchDLS2Class IpatchDLS2Class; #include #include #include #include #include #include #define IPATCH_TYPE_DLS2 (ipatch_dls2_get_type ()) #define IPATCH_DLS2(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_DLS2, IpatchDLS2)) #define IPATCH_DLS2_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_DLS2, IpatchDLS2Class)) #define IPATCH_IS_DLS2(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_DLS2)) #define IPATCH_IS_DLS2_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_DLS2)) #define IPATCH_DLS2_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_DLS2, IpatchDLS2Class)) /* DLS level 2 object */ struct _IpatchDLS2 { /*< public >*/ IpatchBase parent_instance; /* NOTE: This is not the DLS version! Optional descriptive stamp version */ guint32 ms_version; /* most significant 32 bits of 64 bit version */ guint32 ls_version; /* least significant 32 bits of 64 bit version */ IpatchDLS2Info *info; /* info strings */ GSList *insts; /* list of #IpatchDLS2Inst objects */ GSList *samples; /* list of #IpatchDLS2Sample objects */ guint8 *dlid; /* 16 bytes or NULL - globally unique ID (indicates changes) */ }; /* DLS level 2 class */ struct _IpatchDLS2Class { IpatchBaseClass parent_class; }; /** * IpatchDLS2Flags: * @IPATCH_DLS2_VERSION_SET: Set if the ms_version/ls_version values are valid. */ typedef enum { IPATCH_DLS2_VERSION_SET = 1 << IPATCH_BASE_UNUSED_FLAG_SHIFT } IpatchDLS2Flags; /* reserve a couple flags for expansion */ /** * IPATCH_DLS2_UNUSED_FLAG_SHIFT: (skip) */ #define IPATCH_DLS2_UNUSED_FLAG_SHIFT (IPATCH_BASE_UNUSED_FLAG_SHIFT + 4) GType ipatch_dls2_get_type(void); IpatchDLS2 *ipatch_dls2_new(void); #define ipatch_dls2_get_insts(dls) \ ipatch_container_get_children (IPATCH_CONTAINER (dls), \ IPATCH_TYPE_DLS2_INST) #define ipatch_dls2_get_samples(dls) \ ipatch_container_get_children (IPATCH_CONTAINER (dls), \ IPATCH_TYPE_DLS2_SAMPLE) void ipatch_dls2_set_file(IpatchDLS2 *dls, IpatchDLSFile *file); IpatchDLSFile *ipatch_dls2_get_file(IpatchDLS2 *dls); char *ipatch_dls2_get_info(IpatchDLS2 *dls, guint32 fourcc); void ipatch_dls2_set_info(IpatchDLS2 *dls, guint32 fourcc, const char *val); char *ipatch_dls2_make_unique_name(IpatchDLS2 *dls, GType child_type, const char *name, const IpatchItem *exclude); IpatchDLS2Inst *ipatch_dls2_find_inst(IpatchDLS2 *dls, const char *name, int bank, int program, const IpatchDLS2Inst *exclude); IpatchDLS2Sample *ipatch_dls2_find_sample(IpatchDLS2 *dls, const char *name, const IpatchDLS2Sample *exclude); IpatchList *ipatch_dls2_get_region_references(IpatchDLS2Sample *sample); #endif libinstpatch-1.1.6/libinstpatch/IpatchDLS2Conn.c000066400000000000000000000162761400263525300215220ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * connify it under the terms of the GNU Conneral Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Conneral Public License for more details. * * You should have received a copy of the GNU Conneral Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchDLS2Conn * @short_description: DLS version 2 connection structures and functions * @see_also: * @stability: Stable * * Defines structures and functions used for DLS version 2 instrument * parameters (called connections in DLS terminology). */ #include #include #include "IpatchDLS2Conn.h" #include "ipatch_priv.h" /** * ipatch_dls2_conn_get_type: * * Get the #IpatchDLS2Conn boxed type * * Returns: Boxed GType of the #IpatchDLS2Conn structure */ GType ipatch_dls2_conn_get_type(void) { static GType type = 0; if(!type) type = g_boxed_type_register_static("IpatchDLS2Conn", (GBoxedCopyFunc)ipatch_dls2_conn_duplicate, (GBoxedFreeFunc)ipatch_dls2_conn_free); return (type); } /** * ipatch_dls2_conn_new: (skip) * * Create a new connection * * Returns: New connection */ IpatchDLS2Conn * ipatch_dls2_conn_new(void) { return (g_slice_new0(IpatchDLS2Conn)); } /** * ipatch_dls2_conn_free: (skip) * @conn: Connection to free, should not be referenced by any zones. * * Free an #IpatchDLS2Conn structure */ void ipatch_dls2_conn_free(IpatchDLS2Conn *conn) { g_slice_free(IpatchDLS2Conn, conn); } /** * ipatch_dls2_conn_duplicate: (skip) * @conn: DLS connection to duplicate * * Duplicate a connection * * Returns: New duplicate connection */ IpatchDLS2Conn * ipatch_dls2_conn_duplicate(const IpatchDLS2Conn *conn) { IpatchDLS2Conn *newconn; g_return_val_if_fail(conn != NULL, NULL); newconn = ipatch_dls2_conn_new(); newconn->src = conn->src; newconn->ctrlsrc = conn->ctrlsrc; newconn->dest = conn->dest; newconn->trans = conn->trans; newconn->scale = conn->scale; return (newconn); } /** * ipatch_dls2_conn_list_set: * @list: (element-type IpatchDLS2Conn) (transfer none): Pointer to the root pointer of a * connection list * @conn: DLS connection to set in @list * * Set a connection in a connection list. The connection list is searched for * any existing identical connection (same source, control and destination). * If an identical connection is found, its values are overwritten with the * new values, otherwise a new connection is added to the list and the values * copied to it. */ void ipatch_dls2_conn_list_set(GSList **list, const IpatchDLS2Conn *conn) { GSList *p, *last = NULL; IpatchDLS2Conn *c; g_return_if_fail(list != NULL); g_return_if_fail(conn != NULL); p = *list; while(p) { c = (IpatchDLS2Conn *)(p->data); if(IPATCH_DLS2_CONN_ARE_IDENTICAL(c, conn)) { break; } last = p; p = g_slist_next(p); } if(!p) /* duplicate not found? */ { c = ipatch_dls2_conn_duplicate(conn); if(last) { last = g_slist_append(last, c); /* assign to supress gcc warning */ } else { *list = g_slist_append(NULL, c); } } else { *c = *conn; /* overwrite old connection values */ } } /** * ipatch_dls2_conn_list_unset: * @list: (element-type IpatchDLS2Conn) (transfer none): Pointer to the root pointer of a * connection list * @conn: DLS connection to remove from @list * * Remove a connection from a connection list. The connection list is * searched for an identical connection to @conn (same source, * control and destination). If a match is found, it is removed, otherwise * nothing. This essentially sets a connection to its default value, for * those connections which are defined. */ void ipatch_dls2_conn_list_unset(GSList **list, const IpatchDLS2Conn *conn) { IpatchDLS2Conn *c; GSList *p, *prev = NULL; g_return_if_fail(list != NULL); g_return_if_fail(conn != NULL); p = *list; while(p) { c = (IpatchDLS2Conn *)(p->data); if(IPATCH_DLS2_CONN_ARE_IDENTICAL(c, conn)) { /* free and remove connection */ ipatch_dls2_conn_free(c); if(!prev) { *list = p->next; } else { prev->next = p->next; } g_slist_free_1(p); return; } prev = p; p = g_slist_next(p); } } /** * ipatch_dls2_conn_list_duplicate: * @list: (element-type IpatchDLS2Conn) (transfer none): GSList of #IpatchDLS2Conn structures * to duplicate * * Duplicates a connection list (GSList and connection data). * * Returns: (element-type IpatchDLS2Conn) (transfer full): New duplicate connection list which * should be freed with ipatch_dls2_conn_list_free() when finished with it. */ GSList * ipatch_dls2_conn_list_duplicate(const GSList *list) { GSList *newlist = NULL; while(list) { newlist = g_slist_prepend(newlist, ipatch_dls2_conn_duplicate ((IpatchDLS2Conn *)(list->data))); list = list->next; } return (g_slist_reverse(newlist)); } /** * ipatch_dls2_conn_list_duplicate_fast: (skip) * @list: (element-type IpatchDLS2Conn): GSList of #IpatchDLS2Conn structures * to duplicate * * Like ipatch_dls2_conn_list_duplicate() but optimized for speed, new list * is backwards from original. * * Returns: (element-type IpatchDLS2Conn) (transfer full): New duplicate connection list which * should be freed with ipatch_dls2_conn_list_free() when finished with it. */ GSList * ipatch_dls2_conn_list_duplicate_fast(const GSList *list) { GSList *newlist = NULL; while(list) { newlist = g_slist_prepend(newlist, ipatch_dls2_conn_duplicate ((IpatchDLS2Conn *)(list->data))); list = list->next; } return (newlist); } /** * ipatch_dls2_conn_list_free: (skip) * @list: (element-type IpatchDLS2Conn): GSList of #IpatchDLS2Conn structures * to free * @free_conns: If %TRUE then the connections themselves are freed, %FALSE * makes this function act just like g_slist_free() (only the list is * freed not the connections). * * Free a list of connections */ void ipatch_dls2_conn_list_free(GSList *list, gboolean free_conns) { GSList *p; if(free_conns) { p = list; while(p) { ipatch_dls2_conn_free((IpatchDLS2Conn *)(p->data)); p = g_slist_delete_link(p, p); } } else { g_slist_free(list); } } libinstpatch-1.1.6/libinstpatch/IpatchDLS2Conn.h000066400000000000000000000163321400263525300215200ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_DLS2_CONN_H__ #define __IPATCH_DLS2_CONN_H__ #include #include /* forward type declarations */ typedef struct _IpatchDLS2Conn IpatchDLS2Conn; typedef struct _IpatchDLS2ConnInfo IpatchDLS2ConnInfo; /* IpatchDLS2Conn has a GObject boxed type */ #define IPATCH_TYPE_DLS2_CONN (ipatch_dls2_conn_get_type ()) /* DLS2 connection (to set parameter values and define modulators) */ struct _IpatchDLS2Conn { /*< public >*/ guint16 src; /* source enum */ guint16 ctrlsrc; /* second source enum */ guint16 dest; /* destination enum */ guint16 trans; /* transform enum */ gint32 scale; /* scale value */ }; /* Compare two DLS connections to see if they are identical (source, control and dest are identical) */ #define IPATCH_DLS2_CONN_ARE_IDENTICAL(a, b) \ ((a)->src == (b)->src && (a)->ctrlsrc == (b)->ctrlsrc \ && (a)->dest == (b)->dest) /* connection info and constraints structure */ struct _IpatchDLS2ConnInfo { /*< public >*/ guint16 type; /* IpatchDLS2ConnDestType */ gint32 min; /* minimum value allowed */ gint32 max; /* maximum value allowed */ gint32 def; /* default value */ int unit; /* #IpatchUnitType type */ char *label; /* short descriptive label */ char *descr; /* more complete description */ }; /* source connection types */ typedef enum { IPATCH_DLS2_CONN_SRC_NONE = 0, /* No source */ IPATCH_DLS2_CONN_SRC_LFO = 1, /* Modulation LFO */ IPATCH_DLS2_CONN_SRC_VELOCITY = 2, /* MIDI Note-On velocity */ IPATCH_DLS2_CONN_SRC_NOTE = 3, /* MIDI Note number */ IPATCH_DLS2_CONN_SRC_EG1 = 4, /* Envelope Generator 1 */ IPATCH_DLS2_CONN_SRC_EG2 = 5, /* Envelope Generator 2 */ IPATCH_DLS2_CONN_SRC_PITCH_WHEEL = 6, /* Pitch Wheel */ IPATCH_DLS2_CONN_SRC_POLY_PRESSURE = 7, /* Polyphonic pressure */ IPATCH_DLS2_CONN_SRC_CHANNEL_PRESSURE = 8, /* Channel Pressure */ IPATCH_DLS2_CONN_SRC_VIBRATO = 9, /* Vibrato LFO */ /* defined MIDI controller sources */ IPATCH_DLS2_CONN_SRC_CC1 = 0x0081, /* Modulation */ IPATCH_DLS2_CONN_SRC_CC7 = 0x0087, /* Volume */ IPATCH_DLS2_CONN_SRC_CC10 = 0x008A, /* Pan */ IPATCH_DLS2_CONN_SRC_CC11 = 0x008B, /* Expression */ IPATCH_DLS2_CONN_SRC_CC91 = 0x00DB, /* Chorus Send */ IPATCH_DLS2_CONN_SRC_CC93 = 0x00DD, /* Reverb Send */ /* MIDI registered parameter numbers */ IPATCH_DLS2_CONN_SRC_RPN0 = 0x0100, /* Pitch bend range */ IPATCH_DLS2_CONN_SRC_RPN1 = 0x0101, /* Fine tune */ IPATCH_DLS2_CONN_SRC_RPN2 = 0x0102 /* Coarse tune */ } IpatchDLS2ConnSrcType; /* destination connection types */ typedef enum { IPATCH_DLS2_CONN_DEST_NONE = 0, IPATCH_DLS2_CONN_DEST_GAIN = 1, IPATCH_DLS2_CONN_DEST_RESERVED = 2, IPATCH_DLS2_CONN_DEST_PITCH = 3, IPATCH_DLS2_CONN_DEST_PAN = 4, IPATCH_DLS2_CONN_DEST_NOTE = 5, IPATCH_DLS2_CONN_DEST_LEFT = 0x0010, IPATCH_DLS2_CONN_DEST_RIGHT = 0x0011, IPATCH_DLS2_CONN_DEST_CENTER = 0x0012, IPATCH_DLS2_CONN_DEST_LFE_CHANNEL = 0x0013, IPATCH_DLS2_CONN_DEST_LEFT_REAR = 0x0014, IPATCH_DLS2_CONN_DEST_RIGHT_REAR = 0x0015, IPATCH_DLS2_CONN_DEST_CHORUS = 0x0080, IPATCH_DLS2_CONN_DEST_REVERB = 0x0081, IPATCH_DLS2_CONN_DEST_LFO_FREQ = 0x0104, IPATCH_DLS2_CONN_DEST_LFO_DELAY = 0x0105, IPATCH_DLS2_CONN_DEST_VIB_FREQ = 0x0114, IPATCH_DLS2_CONN_DEST_VIB_DELAY = 0x0115, IPATCH_DLS2_CONN_DEST_EG1_ATTACK = 0x0206, IPATCH_DLS2_CONN_DEST_EG1_DECAY = 0x0207, IPATCH_DLS2_CONN_DEST_EG1_RESERVED = 0x0208, IPATCH_DLS2_CONN_DEST_EG1_RELEASE = 0x0209, IPATCH_DLS2_CONN_DEST_EG1_SUSTAIN = 0x020A, IPATCH_DLS2_CONN_DEST_EG1_DELAY = 0x020B, IPATCH_DLS2_CONN_DEST_EG1_HOLD = 0x020C, IPATCH_DLS2_CONN_DEST_EG1_SHUTDOWN = 0x020D, IPATCH_DLS2_CONN_DEST_EG2_ATTACK = 0x030A, IPATCH_DLS2_CONN_DEST_EG2_DECAY = 0x030B, IPATCH_DLS2_CONN_DEST_EG2_RESERVED = 0x030C, IPATCH_DLS2_CONN_DEST_EG2_RELEASE = 0x030D, IPATCH_DLS2_CONN_DEST_EG2_SUSTAIN = 0x030E, IPATCH_DLS2_CONN_DEST_EG2_DELAY = 0x030F, IPATCH_DLS2_CONN_DEST_EG2_HOLD = 0x0310, IPATCH_DLS2_CONN_DEST_FILTER_CUTOFF = 0x0500, IPATCH_DLS2_CONN_DEST_FILTER_Q = 0x0501 } IpatchDLS2ConnDestType; #define IPATCH_DLS2_CONN_OUTPUT_TRANS_NONE 0 /* connection transform types */ typedef enum { IPATCH_DLS2_CONN_TRANS_LINEAR = 0, IPATCH_DLS2_CONN_TRANS_CONCAVE = 1, IPATCH_DLS2_CONN_TRANS_CONVEX = 2, IPATCH_DLS2_CONN_TRANS_SWITCH = 3 } IpatchDLS2ConnTransformType; /* connection polarity types */ typedef enum { IPATCH_DLS2_CONN_POLARITY_UNI = 0, IPATCH_DLS2_CONN_POLARITY_BI = 1 } IpatchDLS2ConnPolarityType; /* masks for IpatchDLS2Conn->trans field */ typedef enum { IPATCH_DLS2_CONN_MASK_OUTPUT_TRANS = 0x000F, /* Output transform mask */ IPATCH_DLS2_CONN_MASK_CTRLSRC_TRANS = 0x00F0, /* Control transform mask */ IPATCH_DLS2_CONN_MASK_CTRLSRC_POLARITY = 0x0100, /* Control polarity mask */ IPATCH_DLS2_CONN_MASK_CTRLSRC_INVERT = 0x0200, /* Control invert mask */ IPATCH_DLS2_CONN_MASK_SRC_TRANS = 0x3C00, /* Source transform mask */ IPATCH_DLS2_CONN_MASK_SRC_POLARITY = 0x4000, /* Source polarity mask */ IPATCH_DLS2_CONN_MASK_SRC_INVERT = 0x8000 /* Source invert mask */ } IpatchDLS2ConnTransformMasks; /* bit shifts for IpatchDLS2Conn->trans field */ typedef enum { IPATCH_DLS2_CONN_SHIFT_OUTPUT_TRANS = 0, /* Output transform shift */ IPATCH_DLS2_CONN_SHIFT_CTRLSRC_TRANS = 4, /* Control transform shift */ IPATCH_DLS2_CONN_SHIFT_CTRLSRC_POLARITY = 8, /* Control polarity shift */ IPATCH_DLS2_CONN_SHIFT_CTRLSRC_INVERT = 9, /* Control invert shift */ IPATCH_DLS2_CONN_SHIFT_SRC_TRANS = 10, /* Source transform shift */ IPATCH_DLS2_CONN_SHIFT_SRC_POLARITY = 14, /* Source polarity shift */ IPATCH_DLS2_CONN_SHIFT_SRC_INVERT = 15 /* Source invert shift */ } IpatchDLS2ConnTransformShifts; extern IpatchDLS2ConnInfo ipatch_dls2_conn_info[]; GType ipatch_dls2_conn_get_type(void); IpatchDLS2Conn *ipatch_dls2_conn_new(void); void ipatch_dls2_conn_free(IpatchDLS2Conn *conn); IpatchDLS2Conn *ipatch_dls2_conn_duplicate(const IpatchDLS2Conn *conn); void ipatch_dls2_conn_list_set(GSList **list, const IpatchDLS2Conn *conn); void ipatch_dls2_conn_list_unset(GSList **list, const IpatchDLS2Conn *conn); GSList *ipatch_dls2_conn_list_duplicate(const GSList *list); GSList *ipatch_dls2_conn_list_duplicate_fast(const GSList *list); void ipatch_dls2_conn_list_free(GSList *list, gboolean free_conns); #endif libinstpatch-1.1.6/libinstpatch/IpatchDLS2Info.c000066400000000000000000000374441400263525300215200ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchDLS2Info * @short_description: DLS version 2 info functions and structure * @see_also: * @stability: Stable * * Structure and functions used for storing DLS informational properties at * many levels of the format. */ #include #include #include #include #include "IpatchDLS2Info.h" #include "IpatchParamProp.h" #include "ipatch_priv.h" /* list bag for associating a hash with an object class that has IpatchDLS2Info properties */ typedef struct { GObjectClass *obj_class; GHashTable *prop_hash; } HashListBag; static void _ipatch_DLS2_infos_free_infos(HashListBag *bag); static void install_prop_helper(GObjectClass *obj_class, guint property_id, GParamSpec *pspec, GHashTable *hash); /* list of GHashTable (property_id -> GParamSpec *) to speed up info property notifies */ static GSList *info_hash_list = NULL; /* ----- Initialization/deinitialization of DLS storing infos structure ----*/ /* Initialize list */ void _ipatch_DLS2_infos_init(void) { info_hash_list = NULL; } /* Free list */ void _ipatch_DLS2_infos_deinit() { g_slist_free_full(info_hash_list, (GDestroyNotify)_ipatch_DLS2_infos_free_infos); } /* Free list data */ static void _ipatch_DLS2_infos_free_infos(HashListBag *bag) { g_hash_table_destroy(bag->prop_hash); g_free(bag); } /* ----- API for DLS storing infos structure -------------------------------*/ /** * ipatch_dls2_info_get: * @info: DLS2 info list * @fourcc: FOURCC info ID * * Gets the value of the info specified by the @fourcc ID from an @info list. * * Returns: Newly allocated info string value or %NULL if the specified info * is not set. Should be freed when no longer needed. */ char * ipatch_dls2_info_get(IpatchDLS2Info *info, guint32 fourcc) { const char *value; value = ipatch_dls2_info_peek(info, fourcc); if(value) { return (g_strdup(value)); } else { return (NULL); } } /** * ipatch_dls2_info_peek: * @info: DLS2 info list * @fourcc: FOURCC info ID * * Gets the value of the info specified by the @fourcc ID from an @info list. * Like ipatch_dls2_info_get but returns the string value without duplicating * it. * * Returns: (transfer none): Info string value or %NULL if the specified info is not * set. Value is internal and should not be modified or freed. */ G_CONST_RETURN char * ipatch_dls2_info_peek(IpatchDLS2Info *info, guint32 fourcc) { GSList *p = info; IpatchDLS2InfoBag *bag; while(p) { bag = (IpatchDLS2InfoBag *)(p->data); if(bag->fourcc == fourcc) { return (bag->value); } p = g_slist_next(p); } return (NULL); } /** * ipatch_dls2_info_set: * @info: DLS2 info list * @fourcc: FOURCC info ID * @value: (nullable): String value to set info to or %NULL to unset * * Sets the info specified by the @fourcc ID in an @info list to a * string @value. */ void ipatch_dls2_info_set(IpatchDLS2Info **info, guint32 fourcc, const char *value) { GSList *p, *last = NULL; IpatchDLS2InfoBag *bag; p = *info; while(p) /* search for existing info with fourcc ID */ { bag = (IpatchDLS2InfoBag *)(p->data); if(bag->fourcc == fourcc) { /* found the info by foucc ID */ g_free(bag->value); if(!value) /* unset the value? */ { *info = g_slist_delete_link(*info, p); ipatch_dls2_info_bag_free(bag); } else { bag->value = g_strdup(value); /* set the value */ } return; } last = p; p = g_slist_next(p); } if(!value) { return; /* no value to unset */ } bag = ipatch_dls2_info_bag_new(); bag->fourcc = fourcc; bag->value = g_strdup(value); if(last) { last = g_slist_append(last, bag); /* info list not empty? assign to keep gcc happy */ } else { *info = g_slist_append(NULL, bag); } } /** * ipatch_dls2_info_free: * @info: DLS2 info list * * Free a DLS info list. */ void ipatch_dls2_info_free(IpatchDLS2Info *info) { GSList *p = info; IpatchDLS2InfoBag *bag; while(p) { bag = (IpatchDLS2InfoBag *)(p->data); g_free(bag->value); ipatch_dls2_info_bag_free(bag); p = g_slist_delete_link(p, p); } } /** * ipatch_dls2_info_duplicate: * @info: DLS2 info list to duplicate * * Duplicate a DLS2 info list. * * Returns: (transfer full): Newly created info list or %NULL if @info was NULL. Free it with * ipatch_dls2_info_free() when finished with it. */ IpatchDLS2Info * ipatch_dls2_info_duplicate(IpatchDLS2Info *info) { GSList *newinfo = NULL, *p = info; IpatchDLS2InfoBag *newbag, *bag; while(p) { bag = (IpatchDLS2InfoBag *)(p->data); newbag = ipatch_dls2_info_bag_new(); newbag->fourcc = bag->fourcc; newbag->value = g_strdup(bag->value); newinfo = g_slist_prepend(newinfo, newbag); p = g_slist_next(p); } return (g_slist_reverse(newinfo)); } /** * ipatch_dls2_info_is_defined: * @fourcc: FOURCC INFO id to check if defined * * Checks if a FOURCC INFO id is a defined INFO id. * * Returns: %TRUE if @fourcc INFO id is defined, %FALSE otherwise */ gboolean ipatch_dls2_info_is_defined(guint32 fourcc) { switch(fourcc) { case IPATCH_DLS2_NAME: case IPATCH_DLS2_DATE: case IPATCH_DLS2_ENGINEER: case IPATCH_DLS2_PRODUCT: case IPATCH_DLS2_COPYRIGHT: case IPATCH_DLS2_COMMENT: case IPATCH_DLS2_SOFTWARE: case IPATCH_DLS2_ARCHIVE_LOCATION: case IPATCH_DLS2_ARTIST: case IPATCH_DLS2_COMMISSIONED: case IPATCH_DLS2_GENRE: case IPATCH_DLS2_KEYWORDS: case IPATCH_DLS2_MEDIUM: case IPATCH_DLS2_SUBJECT: case IPATCH_DLS2_SOURCE: case IPATCH_DLS2_SOURCE_FORM: case IPATCH_DLS2_TECHNICIAN: return (TRUE); default: return (FALSE); } } /** * ipatch_dls2_info_install_class_properties: * @obj_class: GObjectClass to install INFO properties on * * Installs INFO properties for the supplied @obj_class. Used for * class construction of objects implementing IpatchDLS2InfoType * properties. */ void ipatch_dls2_info_install_class_properties(GObjectClass *obj_class) { HashListBag *bag; GHashTable *hash; hash = g_hash_table_new(NULL, NULL); bag = g_new(HashListBag, 1); bag->obj_class = obj_class; bag->prop_hash = hash; info_hash_list = g_slist_prepend(info_hash_list, bag); install_prop_helper(obj_class, IPATCH_DLS2_NAME, g_param_spec_string("name", _("Name"), _("Name"), _("untitled"), G_PARAM_READWRITE | IPATCH_PARAM_UNIQUE), hash); install_prop_helper(obj_class, IPATCH_DLS2_DATE, g_param_spec_string("date", _("Date"), _("Creation date (YYYY-MM-DD)"), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_ENGINEER, g_param_spec_string("engineer", _("Engineer"), _("Engineers separated by \"; \""), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_PRODUCT, g_param_spec_string("product", _("Product"), _("Product intended for"), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_COPYRIGHT, g_param_spec_string("copyright", _("Copyright"), _("Copyright"), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_COMMENT, g_param_spec_string("comment", _("Comments"), _("Comments"), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_SOFTWARE, g_param_spec_string("software", _("Software"), _("Editor software used"), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_ARCHIVE_LOCATION, g_param_spec_string("archive-location", _("Archive Location"), _("Location where subject is archived"), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_ARTIST, g_param_spec_string("artist", _("Artist"), _("Original artist"), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_COMMISSIONED, g_param_spec_string("commissioned", _("Commissioned"), _("Who commissioned the material"), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_GENRE, g_param_spec_string("genre", _("Genre"), _("Genre"), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_KEYWORDS, g_param_spec_string("keywords", _("Keywords"), _("Keywords (separated by \"; \")"), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_MEDIUM, g_param_spec_string("medium", _("Medium"), _("Original medium of the material (record, CD, etc)"), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_SUBJECT, g_param_spec_string("subject", _("Subject"), _("Subject of the material"), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_SOURCE, g_param_spec_string("source", _("Source"), _("Source of the original material"), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_SOURCE_FORM, g_param_spec_string("source-form", _("Source form"), _("Original source that was digitized"), NULL, G_PARAM_READWRITE), hash); install_prop_helper(obj_class, IPATCH_DLS2_TECHNICIAN, g_param_spec_string("technician", _("Technician"), _("Technician who sampled the material"), NULL, G_PARAM_READWRITE), hash); } /* helper function to hash property_id->pspec and install the property also */ static void install_prop_helper(GObjectClass *obj_class, guint property_id, GParamSpec *pspec, GHashTable *hash) { g_hash_table_insert(hash, GUINT_TO_POINTER(property_id), pspec); g_object_class_install_property(obj_class, property_id, pspec); } /** * ipatch_dls2_info_set_property: * @info_list: Pointer to a list of #IpatchDLS2Info structures * @property_id: FOURCC INFO property id to set value of * @value: A string GValue to set INFO value to * * A function used by object set_property methods that implement a * #IpatchDLS2Info list to set an INFO property. * * Returns: %TRUE if @property_id is a valid INFO id, %FALSE otherwise */ gboolean ipatch_dls2_info_set_property(GSList **info_list, guint property_id, const GValue *value) { if(ipatch_dls2_info_is_defined(property_id)) { ipatch_dls2_info_set(info_list, property_id, g_value_get_string(value)); return (TRUE); } else { return (FALSE); } } /** * ipatch_dls2_info_get_property: * @info_list: A list of #IpatchDLS2Info structures * @property_id: FOURCC INFO property id to get value of * @value: A string GValue to store the value of the info to * * A function used by object set_property methods that implement a * #IpatchDLS2Info list to get an INFO property. * * Returns: %TRUE if @property_id is a valid INFO id, %FALSE otherwise */ gboolean ipatch_dls2_info_get_property(GSList *info_list, guint property_id, GValue *value) { if(ipatch_dls2_info_is_defined(property_id)) { g_value_set_string(value, ipatch_dls2_info_get(info_list, property_id)); return (TRUE); } else { return (FALSE); } } /** * ipatch_dls2_info_notify: * @item: Item with INFO properties to notify property change on * @fourcc: FOURCC property ID of info that has changed * @new_value: New value assigned to the property * @old_value: Old value of property * * Notify a changed INFO property on @item for the given fourcc ID. * A convenience function to objects that implement a #IpatchDLS2Info list. */ void ipatch_dls2_info_notify(IpatchItem *item, guint32 fourcc, const GValue *new_value, const GValue *old_value) { GHashTable *found_prop_hash = NULL; GObjectClass *obj_class; GParamSpec *found_pspec = NULL; GSList *p; g_return_if_fail(IPATCH_IS_ITEM(item)); g_return_if_fail(G_IS_VALUE(new_value)); g_return_if_fail(G_IS_VALUE(old_value)); obj_class = G_OBJECT_GET_CLASS(item); /* search for property hash table for the object's class */ for(p = info_hash_list; p; p = p->next) { if(((HashListBag *)(p->data))->obj_class == obj_class) { found_prop_hash = ((HashListBag *)(p->data))->prop_hash; break; } } g_return_if_fail(found_prop_hash); found_pspec = g_hash_table_lookup(found_prop_hash, GUINT_TO_POINTER(fourcc)); g_return_if_fail(found_pspec != NULL); ipatch_item_prop_notify(item, found_pspec, new_value, old_value); } /** * ipatch_dls2_info_bag_new: (skip) * * Create a new DLS info bag structure. * * Returns: Newly allocated info bag. */ IpatchDLS2InfoBag * ipatch_dls2_info_bag_new(void) { return (g_slice_new0(IpatchDLS2InfoBag)); } /** * ipatch_dls2_info_bag_free: (skip) * @bag: Info bag structure to free * * Free a DLS info bag allocated with ipatch_dls2_info_bag_new(). */ void ipatch_dls2_info_bag_free(IpatchDLS2InfoBag *bag) { g_return_if_fail(bag != NULL); g_slice_free(IpatchDLS2InfoBag, bag); } libinstpatch-1.1.6/libinstpatch/IpatchDLS2Info.h000066400000000000000000000066631400263525300215240ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_DLS2_INFO_H__ #define __IPATCH_DLS2_INFO_H__ #include #include #include #include typedef GSList IpatchDLS2Info; typedef struct _IpatchDLS2InfoBag IpatchDLS2InfoBag; /* a container for a INFO ID and value (generally not accessed directly) */ struct _IpatchDLS2InfoBag { guint32 fourcc; /* FOURCC int ID */ char *value; /* info string value */ }; /* known DLS2 INFO FOURCC IDs */ typedef enum { IPATCH_DLS2_ARCHIVE_LOCATION = IPATCH_DLS_FOURCC_IARL, IPATCH_DLS2_ARTIST = IPATCH_DLS_FOURCC_IART, IPATCH_DLS2_COMMISSIONED = IPATCH_DLS_FOURCC_ICMS, IPATCH_DLS2_COMMENT = IPATCH_DLS_FOURCC_ICMT, IPATCH_DLS2_COPYRIGHT = IPATCH_DLS_FOURCC_ICOP, IPATCH_DLS2_DATE = IPATCH_DLS_FOURCC_ICRD, IPATCH_DLS2_ENGINEER = IPATCH_DLS_FOURCC_IENG, IPATCH_DLS2_GENRE = IPATCH_DLS_FOURCC_IGNR, IPATCH_DLS2_KEYWORDS = IPATCH_DLS_FOURCC_IKEY, IPATCH_DLS2_MEDIUM = IPATCH_DLS_FOURCC_IMED, IPATCH_DLS2_NAME = IPATCH_DLS_FOURCC_INAM, IPATCH_DLS2_PRODUCT = IPATCH_DLS_FOURCC_IPRD, IPATCH_DLS2_SUBJECT = IPATCH_DLS_FOURCC_ISBJ, IPATCH_DLS2_SOFTWARE = IPATCH_DLS_FOURCC_ISFT, IPATCH_DLS2_SOURCE = IPATCH_DLS_FOURCC_ISRC, IPATCH_DLS2_SOURCE_FORM = IPATCH_DLS_FOURCC_ISRF, IPATCH_DLS2_TECHNICIAN = IPATCH_DLS_FOURCC_ITCH } IpatchDLS2InfoType; char *ipatch_dls2_info_get(IpatchDLS2Info *info, guint32 fourcc); G_CONST_RETURN char *ipatch_dls2_info_peek(IpatchDLS2Info *info, guint32 fourcc); void ipatch_dls2_info_set(IpatchDLS2Info **info, guint32 fourcc, const char *value); void ipatch_dls2_info_free(IpatchDLS2Info *info); IpatchDLS2Info *ipatch_dls2_info_duplicate(IpatchDLS2Info *info); gboolean ipatch_dls2_info_is_defined(guint32 fourcc); void ipatch_dls2_info_install_class_properties(GObjectClass *obj_class); gboolean ipatch_dls2_info_set_property(IpatchDLS2Info **info_list, guint property_id, const GValue *value); gboolean ipatch_dls2_info_get_property(IpatchDLS2Info *info_list, guint property_id, GValue *value); void ipatch_dls2_info_notify(IpatchItem *item, guint32 fourcc, const GValue *new_value, const GValue *old_value); IpatchDLS2InfoBag *ipatch_dls2_info_bag_new(void); void ipatch_dls2_info_bag_free(IpatchDLS2InfoBag *bag); #endif libinstpatch-1.1.6/libinstpatch/IpatchDLS2Inst.c000066400000000000000000000451301400263525300215310ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchDLS2Inst * @short_description: DLS instrument object * @see_also: * @stability: Stable * * Defines a DLS instrument object. DLS instruments are the toplevel objects * in the DLS instrument file tree hierarchy. */ #include #include #include #include "IpatchDLS2Inst.h" #include "IpatchDLS2Conn.h" #include "IpatchDLS2Region.h" #include "IpatchDLSFile.h" #include "IpatchDLSFile_priv.h" #include "IpatchParamProp.h" #include "IpatchTypeProp.h" #include "ipatch_priv.h" enum { PROP_0, PROP_TITLE, PROP_BANK, PROP_PROGRAM, PROP_PERCUSSION }; static void ipatch_dls2_inst_class_init(IpatchDLS2InstClass *klass); static void ipatch_dls2_inst_finalize(GObject *gobject); static void get_title(IpatchDLS2Inst *inst, GValue *value); static void ipatch_dls2_inst_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_dls2_inst_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_dls2_inst_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static const GType *ipatch_dls2_inst_container_child_types(void); static gboolean ipatch_dls2_inst_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type); static gpointer parent_class = NULL; static GType inst_child_types[2] = { 0 }; GType ipatch_dls2_inst_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchDLS2InstClass), NULL, NULL, (GClassInitFunc)ipatch_dls2_inst_class_init, NULL, NULL, sizeof(IpatchDLS2Inst), 0, (GInstanceInitFunc) NULL, }; item_type = g_type_register_static(IPATCH_TYPE_CONTAINER, "IpatchDLS2Inst", &item_info, 0); } return (item_type); } static void ipatch_dls2_inst_class_init(IpatchDLS2InstClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass); GParamSpec *pspec; parent_class = g_type_class_peek_parent(klass); obj_class->finalize = ipatch_dls2_inst_finalize; obj_class->get_property = ipatch_dls2_inst_get_property; /* we use the IpatchItem item_set_property method */ item_class->item_set_property = ipatch_dls2_inst_set_property; item_class->copy = ipatch_dls2_inst_item_copy; container_class->child_types = ipatch_dls2_inst_container_child_types; container_class->init_iter = ipatch_dls2_inst_container_init_iter; g_object_class_override_property(obj_class, PROP_TITLE, "title"); /* bank and program numbers are flagged as UNIQUE properties and are grouped together (i.e., siblings with same bank/preset are considered conflicting) */ pspec = g_param_spec_int("bank", _("Bank"), _("MIDI bank number"), 0, IPATCH_DLS2_INST_BANK_MAX, 0, G_PARAM_READWRITE | IPATCH_PARAM_UNIQUE); ipatch_param_set(pspec, "unique-group-id", 1, NULL); g_object_class_install_property(obj_class, PROP_BANK, pspec); pspec = g_param_spec_int("program", _("Program"), _("MIDI program number"), 0, 127, 0, G_PARAM_READWRITE | IPATCH_PARAM_UNIQUE); ipatch_param_set(pspec, "unique-group-id", 1, NULL); g_object_class_install_property(obj_class, PROP_PROGRAM, pspec); g_object_class_install_property(obj_class, PROP_PERCUSSION, g_param_spec_boolean("percussion", _("Percussion"), _("Percussion instrument?"), FALSE, G_PARAM_READWRITE)); ipatch_dls2_info_install_class_properties(obj_class); inst_child_types[0] = IPATCH_TYPE_DLS2_REGION; } static void ipatch_dls2_inst_finalize(GObject *gobject) { IpatchDLS2Inst *inst = IPATCH_DLS2_INST(gobject); GSList *p; IPATCH_ITEM_WLOCK(inst); ipatch_dls2_info_free(inst->info); inst->info = NULL; p = inst->conns; while(p) { ipatch_dls2_conn_free((IpatchDLS2Conn *)(p->data)); p = g_slist_delete_link(p, p); } inst->conns = NULL; g_free(inst->dlid); inst->dlid = NULL; IPATCH_ITEM_WUNLOCK(inst); if(G_OBJECT_CLASS(parent_class)->finalize) { G_OBJECT_CLASS(parent_class)->finalize(gobject); } } static void get_title(IpatchDLS2Inst *inst, GValue *value) { char *name; char *s; g_object_get(inst, "name", &name, NULL); if(name) { s = g_strdup_printf("%05d-%03d %s", inst->bank, inst->program, name); g_free(name); } else s = g_strdup_printf("%05d-%03d %s", inst->bank, inst->program, IPATCH_UNTITLED); g_value_take_string(value, s); } static void ipatch_dls2_inst_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchDLS2Inst *inst = IPATCH_DLS2_INST(object); gboolean retval; switch(property_id) { case PROP_BANK: IPATCH_ITEM_WLOCK(inst); /* locked to ensure bank/program atomicity */ inst->bank = g_value_get_int(value); IPATCH_ITEM_WUNLOCK(inst); break; case PROP_PROGRAM: IPATCH_ITEM_WLOCK(inst); /* locked to ensure bank/program atomicity */ inst->program = g_value_get_int(value); IPATCH_ITEM_WUNLOCK(inst); break; case PROP_PERCUSSION: if(g_value_get_boolean(value)) { ipatch_item_set_flags((IpatchItem *)inst, IPATCH_DLS2_INST_PERCUSSION); } else { ipatch_item_clear_flags((IpatchItem *)inst, IPATCH_DLS2_INST_PERCUSSION); } break; default: IPATCH_ITEM_WLOCK(inst); retval = ipatch_dls2_info_set_property(&inst->info, property_id, value); IPATCH_ITEM_WUNLOCK(inst); if(!retval) { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } break; } /* does title property need to be notified? */ if(property_id == PROP_BANK || property_id == PROP_PROGRAM || property_id == IPATCH_DLS2_NAME) { GValue newval = { 0 }; g_value_init(&newval, G_TYPE_STRING); get_title(inst, &newval); ipatch_item_prop_notify((IpatchItem *)inst, ipatch_item_pspec_title, &newval, NULL); g_value_unset(&newval); } } static void ipatch_dls2_inst_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchDLS2Inst *inst = IPATCH_DLS2_INST(object); gboolean retval; switch(property_id) { case PROP_TITLE: get_title(inst, value); break; case PROP_BANK: g_value_set_int(value, inst->bank); break; case PROP_PROGRAM: g_value_set_int(value, inst->program); break; case PROP_PERCUSSION: g_value_set_boolean(value, (ipatch_item_get_flags((IpatchItem *)inst) & IPATCH_DLS2_INST_PERCUSSION) != 0); break; default: IPATCH_ITEM_RLOCK(inst); retval = ipatch_dls2_info_get_property(inst->info, property_id, value); IPATCH_ITEM_RUNLOCK(inst); if(!retval) { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } break; } } static void ipatch_dls2_inst_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchDLS2Inst *src_inst, *dest_inst; IpatchItem *ritem; GSList *p; src_inst = IPATCH_DLS2_INST(src); dest_inst = IPATCH_DLS2_INST(dest); IPATCH_ITEM_RLOCK(src_inst); /* set the percussion flag if set in src */ if(ipatch_item_get_flags(src_inst) & IPATCH_DLS2_INST_PERCUSSION) { ipatch_item_set_flags(dest_inst, IPATCH_DLS2_INST_PERCUSSION); } dest_inst->bank = src_inst->bank; dest_inst->program = src_inst->program; dest_inst->info = ipatch_dls2_info_duplicate(src_inst->info); if(src_inst->dlid) { dest_inst->dlid = g_memdup(src_inst->dlid, IPATCH_DLS_DLID_SIZE); } p = src_inst->regions; while(p) /* duplicate regions */ { /* ++ ref new duplicate item */ ritem = ipatch_item_duplicate_link_func(IPATCH_ITEM(p->data), link_func, user_data); g_return_if_fail(ritem != NULL); /* !! take over duplicate item reference */ dest_inst->regions = g_slist_prepend(dest_inst->regions, ritem); ipatch_item_set_parent(ritem, IPATCH_ITEM(dest_inst)); p = g_slist_next(p); } dest_inst->conns = ipatch_dls2_conn_list_duplicate(src_inst->conns); IPATCH_ITEM_RUNLOCK(src_inst); dest_inst->regions = g_slist_reverse(dest_inst->regions); } static const GType * ipatch_dls2_inst_container_child_types(void) { return (inst_child_types); } /* container is locked by caller */ static gboolean ipatch_dls2_inst_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type) { IpatchDLS2Inst *inst = IPATCH_DLS2_INST(container); if(!g_type_is_a(type, IPATCH_TYPE_DLS2_REGION)) { g_critical("Invalid child type '%s' for parent of type '%s'", g_type_name(type), g_type_name(G_OBJECT_TYPE(container))); return (FALSE); } ipatch_iter_GSList_init(iter, &inst->regions); return (TRUE); } /** * ipatch_dls2_inst_new: * * Create a new DLS instrument object. * * Returns: New DLS instrument with a reference count of 1. Caller * owns the reference and removing it will destroy the item, unless another * reference is added (if its parented for example). */ IpatchDLS2Inst * ipatch_dls2_inst_new(void) { return (IPATCH_DLS2_INST(g_object_new(IPATCH_TYPE_DLS2_INST, NULL))); } /** * ipatch_dls2_inst_first: (skip) * @iter: Patch item iterator containing #IpatchDLS2Inst items * * Gets the first item in an instrument iterator. A convenience wrapper for * ipatch_iter_first(). * * Returns: The first instrument in @iter or %NULL if empty. */ IpatchDLS2Inst * ipatch_dls2_inst_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_DLS2_INST(obj)); } else { return (NULL); } } /** * ipatch_dls2_inst_next: (skip) * @iter: Patch item iterator containing #IpatchDLS2Inst items * * Gets the next item in an instrument iterator. A convenience wrapper for * ipatch_iter_next(). * * Returns: The next instrument in @iter or %NULL if at the end of the list. */ IpatchDLS2Inst * ipatch_dls2_inst_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_DLS2_INST(obj)); } else { return (NULL); } } /** * ipatch_dls2_inst_get_info: * @inst: DLS instrument to get info from * @fourcc: FOURCC integer id of INFO to get * * Get a DLS instrument info string by FOURCC integer ID (integer * representation of a 4 character RIFF chunk ID, see * #IpatchRiff). * * Returns: (transfer full): New allocated info string value or %NULL if no info with the * given @fourcc ID. String should be freed when finished with it. */ char * ipatch_dls2_inst_get_info(IpatchDLS2Inst *inst, guint32 fourcc) { char *val; g_return_val_if_fail(IPATCH_IS_DLS2_INST(inst), NULL); IPATCH_ITEM_RLOCK(inst); val = ipatch_dls2_info_get(inst->info, fourcc); IPATCH_ITEM_RUNLOCK(inst); return (val); } /** * ipatch_dls2_inst_set_info: * @inst: DLS instrument to set info of * @fourcc: FOURCC integer ID of INFO to set * @val: (nullable): Value to set info to or %NULL to unset (clear) info. * * Sets an INFO value in a DLS instrument object. * Emits changed signal. */ void ipatch_dls2_inst_set_info(IpatchDLS2Inst *inst, guint32 fourcc, const char *val) { GValue newval = { 0 }, oldval = { 0 }; g_return_if_fail(IPATCH_IS_DLS2_INST(inst)); g_value_init(&newval, G_TYPE_STRING); g_value_set_static_string(&newval, val); g_value_init(&oldval, G_TYPE_STRING); g_value_take_string(&oldval, ipatch_dls2_inst_get_info(inst, fourcc)); IPATCH_ITEM_WLOCK(inst); ipatch_dls2_info_set(&inst->info, fourcc, val); IPATCH_ITEM_WUNLOCK(inst); ipatch_dls2_info_notify((IpatchItem *)inst, fourcc, &newval, &oldval); /* does title property need to be notified? */ if(fourcc == IPATCH_DLS2_NAME) ipatch_item_prop_notify((IpatchItem *)inst, ipatch_item_pspec_title, &newval, &oldval); g_value_unset(&oldval); g_value_unset(&newval); } /** * ipatch_dls2_inst_set_midi_locale: * @inst: DLS instrument to set MIDI locale of * @bank: MIDI bank number to assign to instrument * @program: MIDI program number to assign to instrument * * Sets the MIDI locale of a DLS instrument (bank and program numbers). */ void ipatch_dls2_inst_set_midi_locale(IpatchDLS2Inst *inst, int bank, int program) { g_object_set(inst, "bank", bank, "program", program, NULL); } /** * ipatch_dls2_inst_get_midi_locale: * @inst: Instrument to get MIDI locale from * @bank: (out) (optional): Location to store instrument's MIDI bank number or %NULL * @program: (out) (optional): Location to store instrument's MIDI program number or %NULL * * Gets the MIDI locale of a DLS instrument (bank and program numbers). */ void ipatch_dls2_inst_get_midi_locale(IpatchDLS2Inst *inst, int *bank, int *program) { g_return_if_fail(IPATCH_IS_DLS2_INST(inst)); IPATCH_ITEM_RLOCK(inst); if(bank) { *bank = inst->bank; } if(program) { *program = inst->program; } IPATCH_ITEM_RUNLOCK(inst); } /** * ipatch_dls2_inst_compare: * @p1: First instrument in comparison * @p2: Second instrument in comparison * * Instrument comparison function for sorting. Compare two instruments by their * MIDI bank:program numbers. Note that this function is compatible with * GCompareFunc and can therefore be used with g_list_sort, etc. * * FIXME: Also note that percussion instruments are sorted after regular ones. * * Returns: Comparison result that is less than, equal to, or greater than zero * if @p1 is found, respectively, to be less than, to match, or be greater * than @p2. */ int ipatch_dls2_inst_compare(const IpatchDLS2Inst *p1, const IpatchDLS2Inst *p2) { gint32 aval, bval; guint32 aperc, bperc; aperc = (ipatch_item_get_flags((IpatchItem *)p1) & IPATCH_DLS2_INST_PERCUSSION) ? 1 << 31 : 0; bperc = (ipatch_item_get_flags((IpatchItem *)p2) & IPATCH_DLS2_INST_PERCUSSION) ? 1 << 31 : 0; aval = aperc | ((gint32)(p1->bank) << 16) | p1->program; bval = bperc | ((gint32)(p2->bank) << 16) | p2->program; return (aval - bval); } /** * ipatch_dls2_inst_get_conns: * @inst: Instrument to get connections from * * Gets a list of connections from a DLS instrument. List should be freed with * ipatch_dls2_conn_list_free() (free_conns set to %TRUE) when finished * with it. * * Returns: (element-type IpatchDLS2Conn) (transfer full): New list of connections * (#IpatchDLS2Conn) in @inst or %NULL if no connections. Remember to free it * when finished. */ GSList * ipatch_dls2_inst_get_conns(IpatchDLS2Inst *inst) { GSList *newlist; g_return_val_if_fail(IPATCH_IS_DLS2_INST(inst), NULL); IPATCH_ITEM_RLOCK(inst); newlist = ipatch_dls2_conn_list_duplicate(inst->conns); IPATCH_ITEM_RUNLOCK(inst); return (newlist); } /** * ipatch_dls2_inst_set_conn: * @inst: DLS instrument * @conn: Connection * * Set a global DLS connection in an instrument. See * ipatch_dls2_conn_list_set() for more details. */ void ipatch_dls2_inst_set_conn(IpatchDLS2Inst *inst, const IpatchDLS2Conn *conn) { g_return_if_fail(IPATCH_IS_DLS2_INST(inst)); g_return_if_fail(conn != NULL); IPATCH_ITEM_WLOCK(inst); ipatch_dls2_conn_list_set(&inst->conns, conn); IPATCH_ITEM_WUNLOCK(inst); } /** * ipatch_dls2_inst_unset_conn: * @inst: DLS instrument * @conn: Connection * * Remove a global DLS connection from an instrument. See * ipatch_dls2_conn_list_unset() for more details. */ void ipatch_dls2_inst_unset_conn(IpatchDLS2Inst *inst, const IpatchDLS2Conn *conn) { g_return_if_fail(IPATCH_IS_DLS2_INST(inst)); g_return_if_fail(conn != NULL); IPATCH_ITEM_WLOCK(inst); ipatch_dls2_conn_list_unset(&inst->conns, conn); IPATCH_ITEM_WUNLOCK(inst); } /** * ipatch_dls2_inst_unset_all_conns: * @inst: DLS instrument * * Remove all global connections in an instrument. */ void ipatch_dls2_inst_unset_all_conns(IpatchDLS2Inst *inst) { g_return_if_fail(IPATCH_IS_DLS2_INST(inst)); IPATCH_ITEM_WLOCK(inst); ipatch_dls2_conn_list_free(inst->conns, TRUE); inst->conns = NULL; IPATCH_ITEM_WUNLOCK(inst); } /** * ipatch_dls2_inst_conn_count: * @inst: Instrument to count connections in * * Count number of connections in a instrument * * Returns: Count of connections */ guint ipatch_dls2_inst_conn_count(IpatchDLS2Inst *inst) { guint i; IPATCH_ITEM_RLOCK(inst); i = g_slist_length(inst->conns); IPATCH_ITEM_RUNLOCK(inst); return (i); } libinstpatch-1.1.6/libinstpatch/IpatchDLS2Inst.h000066400000000000000000000100751400263525300215360ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_DLS2_INST_H__ #define __IPATCH_DLS2_INST_H__ #include #include /* forward type declarations */ typedef struct _IpatchDLS2Inst IpatchDLS2Inst; typedef struct _IpatchDLS2InstClass IpatchDLS2InstClass; #include #include #include #define IPATCH_TYPE_DLS2_INST (ipatch_dls2_inst_get_type ()) #define IPATCH_DLS2_INST(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_DLS2_INST, \ IpatchDLS2Inst)) #define IPATCH_DLS2_INST_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_DLS2_INST, \ IpatchDLS2InstClass)) #define IPATCH_IS_DLS2_INST(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_DLS2_INST)) #define IPATCH_IS_DLS2_INST_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_DLS2_INST)) #define IPATCH_DLS2_INST_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_DLS2_INST, \ IpatchDLS2InstClass)) /* DLS instrument object */ struct _IpatchDLS2Inst { IpatchContainer parent_instance; guint16 bank; /* MIDI locale Bank */ guint16 program; /* MIDI locale Program */ IpatchDLS2Info *info; /* info strings */ GSList *regions; /* list of IpatchDLS2Region structures */ GSList *conns; /* list of global IpatchDLS2Conn structures */ guint8 *dlid; /* 16 byte unique ID or NULL */ }; struct _IpatchDLS2InstClass { IpatchContainerClass parent_class; }; /** * IPATCH_DLS2_INST_BANK_MAX: * Max value for instrument MIDI bank (14 bits = 2 normalized MIDI bytes) */ #define IPATCH_DLS2_INST_BANK_MAX 0x3FFF /** * IpatchDLS2InstFlags: * @IPATCH_DLS2_INST_PERCUSSION: Set if percussion instrument */ typedef enum { IPATCH_DLS2_INST_PERCUSSION = 1 << IPATCH_CONTAINER_UNUSED_FLAG_SHIFT } IpatchDLS2InstFlags; /** * IPATCH_DLS2_INST_UNUSED_FLAG_SHIFT: (skip) */ /* 1 flag */ #define IPATCH_DLS2_INST_UNUSED_FLAG_SHIFT \ (IPATCH_CONTAINER_UNUSED_FLAG_SHIFT + 1) GType ipatch_dls2_inst_get_type(void); IpatchDLS2Inst *ipatch_dls2_inst_new(void); #define ipatch_dls2_inst_get_regions(inst) \ ipatch_container_get_children (IPATCH_CONTAINER (inst), \ IPATCH_TYPE_DLS2_REGION) IpatchDLS2Inst *ipatch_dls2_inst_first(IpatchIter *iter); IpatchDLS2Inst *ipatch_dls2_inst_next(IpatchIter *iter); char *ipatch_dls2_inst_get_info(IpatchDLS2Inst *inst, guint32 fourcc); void ipatch_dls2_inst_set_info(IpatchDLS2Inst *inst, guint32 fourcc, const char *val); void ipatch_dls2_inst_set_midi_locale(IpatchDLS2Inst *inst, int bank, int program); void ipatch_dls2_inst_get_midi_locale(IpatchDLS2Inst *inst, int *bank, int *program); int ipatch_dls2_inst_compare(const IpatchDLS2Inst *p1, const IpatchDLS2Inst *p2); GSList *ipatch_dls2_inst_get_conns(IpatchDLS2Inst *inst); void ipatch_dls2_inst_set_conn(IpatchDLS2Inst *inst, const IpatchDLS2Conn *conn); void ipatch_dls2_inst_unset_conn(IpatchDLS2Inst *inst, const IpatchDLS2Conn *conn); void ipatch_dls2_inst_unset_all_conns(IpatchDLS2Inst *inst); guint ipatch_dls2_inst_conn_count(IpatchDLS2Inst *inst); #endif libinstpatch-1.1.6/libinstpatch/IpatchDLS2Region.c000066400000000000000000001120721400263525300220370ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchDLS2Region * @short_description: DLS region object * @see_also: #IpatchDLSInst * @stability: Stable * * DLS regions are child items of #IpatchDLSInst objects and define how an * individual audio sample is synthesized in an instrument. */ #include #include #include #include "IpatchDLS2Region.h" #include "IpatchGigRegion.h" #include "IpatchRange.h" #include "IpatchSample.h" #include "IpatchTypeProp.h" #include "ipatch_priv.h" enum { PROP_0, PROP_TITLE, PROP_NOTE_RANGE, PROP_VELOCITY_RANGE, PROP_KEY_GROUP, PROP_LAYER_GROUP, PROP_PHASE_GROUP, PROP_CHANNEL, PROP_LINK_ITEM, PROP_SAMPLE_INFO_OVERRIDE, /* sample info override boolean */ /* IpatchItem flags (no one needs to know that though) */ PROP_SELF_NON_EXCLUSIVE, PROP_PHASE_MASTER, PROP_MULTI_CHANNEL, /* IpatchSample interface properties */ PROP_SAMPLE_SIZE, PROP_SAMPLE_FORMAT, PROP_SAMPLE_RATE, PROP_SAMPLE_DATA }; enum { SET_CONN, UNSET_CONN, LAST_SIGNAL }; static void ipatch_dls2_region_sample_iface_init(IpatchSampleIface *sample_iface); static gboolean ipatch_dls2_region_sample_iface_open(IpatchSampleHandle *handle, GError **err); static void ipatch_dls2_region_class_init(IpatchDLS2RegionClass *klass); static void ipatch_dls2_region_init(IpatchDLS2Region *region); static void ipatch_dls2_region_finalize(GObject *gobject); static void ipatch_dls2_region_get_title(IpatchDLS2Region *region, GValue *value); static void ipatch_dls2_region_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_dls2_region_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_dls2_region_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static void ipatch_dls2_region_item_remove_full(IpatchItem *item, gboolean full); static void ipatch_dls2_region_real_set_sample(IpatchDLS2Region *region, IpatchDLS2Sample *sample, gboolean sample_notify); static void ipatch_dls2_region_get_sample_info(IpatchDLS2Region *region, IpatchDLS2SampleInfo *info); /* cached param specs to speed up prop notifies */ static GParamSpec *link_item_pspec; G_DEFINE_TYPE_WITH_CODE(IpatchDLS2Region, ipatch_dls2_region, IPATCH_TYPE_ITEM, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_dls2_region_sample_iface_init)) /* sample interface initialization */ static void ipatch_dls2_region_sample_iface_init(IpatchSampleIface *sample_iface) { sample_iface->open = ipatch_dls2_region_sample_iface_open; sample_iface->loop_types = ipatch_sample_loop_types_standard_release; } static gboolean ipatch_dls2_region_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchDLS2Region *region = IPATCH_DLS2_REGION(handle->sample); g_return_val_if_fail(region->sample != NULL, FALSE); return (ipatch_sample_handle_cascade_open(handle, IPATCH_SAMPLE(region->sample), err)); } static void ipatch_dls2_region_class_init(IpatchDLS2RegionClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->finalize = ipatch_dls2_region_finalize; obj_class->get_property = ipatch_dls2_region_get_property; item_class->item_set_property = ipatch_dls2_region_set_property; item_class->copy = ipatch_dls2_region_item_copy; item_class->remove_full = ipatch_dls2_region_item_remove_full; g_object_class_override_property(obj_class, PROP_TITLE, "title"); g_object_class_install_property(obj_class, PROP_NOTE_RANGE, ipatch_param_spec_range("note-range", _("Note range"), _("MIDI note range"), 0, 127, 0, 127, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_VELOCITY_RANGE, ipatch_param_spec_range("velocity-range", _("Velocity range"), _("MIDI velocity range"), 0, 127, 0, 127, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_KEY_GROUP, g_param_spec_int("key-group", _("Key group"), _("Percussion key group"), 0, 15, 0, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_LAYER_GROUP, g_param_spec_int("layer-group", _("Layer group"), _("Layer group"), 0, G_MAXUSHORT, 0, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_PHASE_GROUP, g_param_spec_int("phase-group", _("Phase group"), _("Phase locked sample group"), 0, G_MAXUSHORT, 0, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_CHANNEL, g_param_spec_int("channel", _("Channel"), _("DLS audio channel identifier"), 0, 0x03FFFF, 0, G_PARAM_READWRITE)); link_item_pspec = g_param_spec_object("link-item", _("Link item"), _("Link item"), IPATCH_TYPE_DLS2_SAMPLE, G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_LINK_ITEM, link_item_pspec); g_object_class_install_property(obj_class, PROP_SAMPLE_INFO_OVERRIDE, g_param_spec_boolean("sample-info-override", _("Override sample info"), _("Override sample info"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_SELF_NON_EXCLUSIVE, g_param_spec_boolean("self-non-exclusive", _("Non exclusive"), _("Self non exclusive"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_PHASE_MASTER, g_param_spec_boolean("phase-master", _("Phase master"), _("Multi channel phase lock master"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_MULTI_CHANNEL, g_param_spec_boolean("multi-channel", _("Multi channel"), _("Multi channel"), FALSE, G_PARAM_READWRITE)); /* IpatchSample interface properties */ ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_SIZE, "sample-size"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_FORMAT, "sample-format"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_RATE, "sample-rate"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_DATA, "sample-data"); ipatch_dls2_info_install_class_properties(obj_class); ipatch_dls2_sample_info_install_class_properties(obj_class); } static void ipatch_dls2_region_init(IpatchDLS2Region *region) { region->note_range_low = 0; region->note_range_high = 127; region->velocity_range_low = 0; region->velocity_range_high = 127; region->key_group = 0; region->layer_group = 0; region->phase_group = 0; region->channel = 0; region->info = NULL; region->sample_info = NULL; region->sample = NULL; region->conns = NULL; } static void ipatch_dls2_region_finalize(GObject *gobject) { IpatchDLS2Region *region = IPATCH_DLS2_REGION(gobject); GSList *p; IPATCH_ITEM_WLOCK(region); if(region->sample_info) { ipatch_dls2_sample_info_free(region->sample_info); region->sample_info = NULL; } if(region->sample) { g_object_unref(region->sample); region->sample = NULL; } p = region->conns; while(p) { ipatch_dls2_conn_free((IpatchDLS2Conn *)(p->data)); p = g_slist_delete_link(p, p); } region->conns = NULL; IPATCH_ITEM_WUNLOCK(region); if(G_OBJECT_CLASS(ipatch_dls2_region_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_dls2_region_parent_class)->finalize(gobject); } } static void ipatch_dls2_region_get_title(IpatchDLS2Region *region, GValue *value) { IpatchDLS2Sample *sample; char *s = NULL; sample = ipatch_dls2_region_get_sample(region); /* ++ ref sample */ if(sample) { g_object_get(sample, "name", &s, NULL); g_object_unref(sample); /* -- unref sample */ } g_value_take_string(value, s); } static void ipatch_dls2_region_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchDLS2Region *region = IPATCH_DLS2_REGION(object); IpatchDLS2SampleInfo saminfo = IPATCH_DLS2_SAMPLE_INFO_INIT; IpatchDLS2SampleInfo oldinfo, newinfo; IpatchRange *range; gboolean is_samprop; gboolean retval; switch(property_id) { case PROP_NOTE_RANGE: range = ipatch_value_get_range(value); if(range) { IPATCH_ITEM_WLOCK(region); region->note_range_low = range->low; region->note_range_high = range->high; IPATCH_ITEM_WUNLOCK(region); } break; case PROP_VELOCITY_RANGE: range = ipatch_value_get_range(value); if(range) { IPATCH_ITEM_WLOCK(region); region->velocity_range_low = range->low; region->velocity_range_high = range->high; IPATCH_ITEM_WUNLOCK(region); } break; case PROP_KEY_GROUP: region->key_group = g_value_get_int(value); break; case PROP_LAYER_GROUP: region->layer_group = g_value_get_int(value); break; case PROP_PHASE_GROUP: region->phase_group = g_value_get_int(value); break; case PROP_CHANNEL: region->channel = g_value_get_int(value); break; case PROP_LINK_ITEM: ipatch_dls2_region_real_set_sample(region, IPATCH_DLS2_SAMPLE (g_value_get_object(value)), FALSE); break; case PROP_SAMPLE_INFO_OVERRIDE: ipatch_dls2_region_get_sample_info(region, &oldinfo); if(g_value_get_boolean(value)) ipatch_item_set_flags((IpatchItem *)region, IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE); else ipatch_item_clear_flags((IpatchItem *)region, IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE); ipatch_dls2_region_get_sample_info(region, &newinfo); ipatch_dls2_sample_info_notify_changes((IpatchItem *)region, &newinfo, &oldinfo); break; case PROP_SELF_NON_EXCLUSIVE: if(g_value_get_boolean(value)) ipatch_item_set_flags(IPATCH_ITEM(object), IPATCH_DLS2_REGION_SELF_NON_EXCLUSIVE); else ipatch_item_clear_flags(IPATCH_ITEM(object), IPATCH_DLS2_REGION_SELF_NON_EXCLUSIVE); break; case PROP_PHASE_MASTER: if(g_value_get_boolean(value)) ipatch_item_set_flags(IPATCH_ITEM(object), IPATCH_DLS2_REGION_PHASE_MASTER); else ipatch_item_clear_flags(IPATCH_ITEM(object), IPATCH_DLS2_REGION_PHASE_MASTER); break; case PROP_MULTI_CHANNEL: if(g_value_get_boolean(value)) ipatch_item_set_flags(IPATCH_ITEM(object), IPATCH_DLS2_REGION_MULTI_CHANNEL); else ipatch_item_clear_flags(IPATCH_ITEM(object), IPATCH_DLS2_REGION_MULTI_CHANNEL); break; default: is_samprop = ipatch_dls2_sample_info_is_property_id_valid(property_id); /* check if region override info valid but override flag not set. If so then copy sample info to static 'saminfo'. OK to test region without locking it (worst that happens is default values get used). */ if(is_samprop && region->sample_info && !(ipatch_item_get_flags(region) & IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE)) { ipatch_dls2_region_get_sample_info(region, &saminfo); } IPATCH_ITEM_WLOCK(region); /* is override sample_info valid but override flag not set and it is in fact a sample info property? - Copy values from sample (or defaults) */ if(is_samprop && region->sample_info && !(ipatch_item_get_flags(region) & IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE)) { *region->sample_info = saminfo; } retval = ipatch_dls2_sample_info_set_property(®ion->sample_info, property_id, value); if(retval) /* sample info set, set override flag */ { ipatch_item_set_flags(region, IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE); } else retval = ipatch_dls2_info_set_property(®ion->info, property_id, value); IPATCH_ITEM_WUNLOCK(region); if(!retval) { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } break; } } static void ipatch_dls2_region_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchDLS2Region *region = IPATCH_DLS2_REGION(object); IpatchDLS2Sample *sample = NULL; IpatchRange range; gboolean bool, retval = 0; gboolean get_from_sample = FALSE; switch(property_id) { case PROP_TITLE: ipatch_dls2_region_get_title(region, value); break; case PROP_NOTE_RANGE: IPATCH_ITEM_RLOCK(region); range.low = region->note_range_low; range.high = region->note_range_high; IPATCH_ITEM_RUNLOCK(region); ipatch_value_set_range(value, &range); break; case PROP_VELOCITY_RANGE: IPATCH_ITEM_RLOCK(region); range.low = region->velocity_range_low; range.high = region->velocity_range_high; IPATCH_ITEM_RUNLOCK(region); ipatch_value_set_range(value, &range); break; case PROP_KEY_GROUP: g_value_set_int(value, region->key_group); break; case PROP_LAYER_GROUP: g_value_set_int(value, region->layer_group); break; case PROP_PHASE_GROUP: g_value_set_int(value, region->phase_group); break; case PROP_CHANNEL: g_value_set_int(value, region->channel); break; case PROP_LINK_ITEM: g_value_take_object(value, ipatch_dls2_region_get_sample(region)); break; case PROP_SAMPLE_INFO_OVERRIDE: g_value_set_boolean(value, (ipatch_item_get_flags((IpatchItem *)region) & IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE) != 0); break; case PROP_SELF_NON_EXCLUSIVE: bool = (ipatch_item_get_flags(IPATCH_ITEM(object)) & IPATCH_DLS2_REGION_SELF_NON_EXCLUSIVE) > 0; g_value_set_boolean(value, bool); break; case PROP_PHASE_MASTER: bool = (ipatch_item_get_flags(IPATCH_ITEM(object)) & IPATCH_DLS2_REGION_PHASE_MASTER) > 0; g_value_set_boolean(value, bool); break; case PROP_MULTI_CHANNEL: bool = (ipatch_item_get_flags(IPATCH_ITEM(object)) & IPATCH_DLS2_REGION_MULTI_CHANNEL) > 0; g_value_set_boolean(value, bool); break; case PROP_SAMPLE_SIZE: sample = ipatch_dls2_region_get_sample(region); /* ++ ref sample */ g_return_if_fail(sample != NULL); g_object_get_property((GObject *)sample, "sample-size", value); g_object_unref(sample); /* -- unref sample */ break; case PROP_SAMPLE_FORMAT: sample = ipatch_dls2_region_get_sample(region); /* ++ ref sample */ g_return_if_fail(sample != NULL); g_object_get_property((GObject *)sample, "sample-size", value); g_object_unref(sample); /* -- unref sample */ break; case PROP_SAMPLE_RATE: sample = ipatch_dls2_region_get_sample(region); /* ++ ref sample */ g_return_if_fail(sample != NULL); g_object_get_property((GObject *)sample, "sample-rate", value); g_object_unref(sample); /* -- unref sample */ break; case PROP_SAMPLE_DATA: sample = ipatch_dls2_region_get_sample(region); /* ++ ref sample */ g_return_if_fail(sample != NULL); g_object_get_property((GObject *)sample, "sample-data", value); g_object_unref(sample); /* -- unref sample */ break; default: IPATCH_ITEM_RLOCK(region); /* a sample info property? */ if(property_id >= IPATCH_DLS2_SAMPLE_INFO_FIRST_PROPERTY_ID && property_id < (IPATCH_DLS2_SAMPLE_INFO_FIRST_PROPERTY_ID + IPATCH_DLS2_SAMPLE_INFO_PROPERTY_COUNT)) { if(ipatch_item_get_flags(region) & IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE && region->sample_info) retval = ipatch_dls2_sample_info_get_property(region->sample_info, property_id, value); else { get_from_sample = TRUE; sample = region->sample ? g_object_ref(region->sample) : NULL; } } /* not sample info, is it a DLS text info property? */ else retval = ipatch_dls2_info_get_property(region->info, property_id, value); IPATCH_ITEM_RUNLOCK(region); if(get_from_sample) /* get sample info from linked sample? */ { if(sample) { IPATCH_ITEM_RLOCK(sample); ipatch_dls2_sample_info_get_property(sample->sample_info, property_id, value); IPATCH_ITEM_RUNLOCK(sample); g_object_unref(sample); } else { ipatch_dls2_sample_info_get_property(NULL, property_id, value); } } else if(!retval) { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } break; } } static void ipatch_dls2_region_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchDLS2Region *src_reg, *dest_reg; IpatchDLS2Sample *refsample; src_reg = IPATCH_DLS2_REGION(src); dest_reg = IPATCH_DLS2_REGION(dest); IPATCH_ITEM_RLOCK(src_reg); /* duplicate the flags */ ipatch_item_set_flags(dest_reg, ipatch_item_get_flags(src_reg) & IPATCH_DLS2_REGION_FLAG_MASK); dest_reg->note_range_low = src_reg->note_range_low; dest_reg->note_range_high = src_reg->note_range_high; dest_reg->velocity_range_low = src_reg->velocity_range_low; dest_reg->velocity_range_high = src_reg->velocity_range_high; dest_reg->key_group = src_reg->key_group; dest_reg->layer_group = src_reg->layer_group; dest_reg->phase_group = src_reg->phase_group; dest_reg->channel = src_reg->channel; dest_reg->info = ipatch_dls2_info_duplicate(src_reg->info); dest_reg->sample_info = src_reg->sample_info ? ipatch_dls2_sample_info_duplicate(src_reg->sample_info) : NULL; /* pass the link to the link handler (if any) */ refsample = (IpatchDLS2Sample *) IPATCH_ITEM_COPY_LINK_FUNC(IPATCH_ITEM(dest_reg), IPATCH_ITEM(src_reg->sample), link_func, user_data); if(refsample) { ipatch_dls2_region_set_sample(dest_reg, refsample); } /* duplicate the connection list */ dest_reg->conns = ipatch_dls2_conn_list_duplicate(src_reg->conns); IPATCH_ITEM_RUNLOCK(src_reg); } static void ipatch_dls2_region_item_remove_full(IpatchItem *item, gboolean full) { if(full) { ipatch_dls2_region_set_sample(IPATCH_DLS2_REGION(item), NULL); } if(IPATCH_ITEM_CLASS(ipatch_dls2_region_parent_class)->remove_full) { IPATCH_ITEM_CLASS(ipatch_dls2_region_parent_class)->remove_full(item, full); } } /** * ipatch_dls2_region_new: * * Create a new DLS region object. * * Returns: Newly created DLS region with a ref count of 1 which the caller * owns. */ IpatchDLS2Region * ipatch_dls2_region_new(void) { return (IPATCH_DLS2_REGION(g_object_new(IPATCH_TYPE_DLS2_REGION, NULL))); } /** * ipatch_dls2_region_first: (skip) * @iter: Patch item iterator containing #IpatchDLS2Region items * * Gets the first item in a region iterator. A convenience * wrapper for ipatch_iter_first(). * * Returns: The first region in @iter or %NULL if empty. */ IpatchDLS2Region * ipatch_dls2_region_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_DLS2_REGION(obj)); } else { return (NULL); } } /** * ipatch_dls2_region_next: (skip) * @iter: Patch item iterator containing #IpatchDLS2Region items * * Gets the next item in a region iterator. A convenience wrapper * for ipatch_iter_next(). * * Returns: The next region in @iter or %NULL if at the end of * the list. */ IpatchDLS2Region * ipatch_dls2_region_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_DLS2_REGION(obj)); } else { return (NULL); } } /** * ipatch_dls2_region_get_info: * @region: DLS region to get info from * @fourcc: FOURCC integer id of INFO to get * * Get a DLS region info string by FOURCC integer ID (integer * representation of a 4 character RIFF chunk ID, see * #IpatchRiff). * * Returns: New allocated info string value or %NULL if no info with the * given @fourcc ID. String should be freed when finished with it. */ char * ipatch_dls2_region_get_info(IpatchDLS2Region *region, guint32 fourcc) { char *val; g_return_val_if_fail(IPATCH_IS_DLS2_REGION(region), NULL); IPATCH_ITEM_RLOCK(region); val = ipatch_dls2_info_get(region->info, fourcc); IPATCH_ITEM_RUNLOCK(region); return (val); } /** * ipatch_dls2_region_set_info: * @region: DLS region to set info of * @fourcc: FOURCC integer ID of INFO to set * @val: (nullable): Value to set info to or %NULL to unset (clear) info. * * Sets an INFO value in a DLS region object. * Emits changed signal. */ void ipatch_dls2_region_set_info(IpatchDLS2Region *region, guint32 fourcc, const char *val) { GValue newval = { 0 }, oldval = { 0 }; g_return_if_fail(IPATCH_IS_DLS2_REGION(region)); g_value_init(&newval, G_TYPE_STRING); g_value_set_static_string(&newval, val); g_value_init(&oldval, G_TYPE_STRING); g_value_take_string(&oldval, ipatch_dls2_region_get_info(region, fourcc)); IPATCH_ITEM_WLOCK(region); ipatch_dls2_info_set(®ion->info, fourcc, val); IPATCH_ITEM_WUNLOCK(region); ipatch_dls2_info_notify((IpatchItem *)region, fourcc, &newval, &oldval); g_value_unset(&oldval); g_value_unset(&newval); } /** * ipatch_dls2_region_set_sample: * @region: Region to set sample of * @sample: Sample to set region to. Should be NULL or a IpatchDLS2Sample object * * Sets the referenced sample of a region. */ void ipatch_dls2_region_set_sample(IpatchDLS2Region *region, IpatchDLS2Sample *sample) { g_return_if_fail(IPATCH_IS_DLS2_REGION(region)); if(sample != NULL) { g_return_if_fail (IPATCH_IS_DLS2_SAMPLE (sample)); } ipatch_dls2_region_real_set_sample(region, sample, TRUE); } static void ipatch_dls2_region_real_set_sample(IpatchDLS2Region *region, IpatchDLS2Sample *sample, gboolean sample_notify) { GValue newval = { 0 }, oldval = { 0 }; IpatchDLS2SampleInfo oldinfo, newinfo; if(sample_notify) ipatch_item_get_property_fast((IpatchItem *)region, link_item_pspec, &oldval); /* get all values of current sample info */ ipatch_dls2_region_get_sample_info(region, &oldinfo); IPATCH_ITEM_WLOCK(region); if(region->sample) { g_object_unref(region->sample); } if(sample) { g_object_ref(sample); } region->sample = sample; IPATCH_ITEM_WUNLOCK(region); if(sample_notify) { g_value_init(&newval, IPATCH_TYPE_DLS2_SAMPLE); g_value_set_object(&newval, sample); ipatch_item_prop_notify((IpatchItem *)region, link_item_pspec, &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } /* notify title property change */ g_value_init(&newval, G_TYPE_STRING); ipatch_dls2_region_get_title(region, &newval); ipatch_item_prop_notify((IpatchItem *)region, ipatch_item_pspec_title, &newval, NULL); g_value_unset(&newval); /* notify for sample info properties */ ipatch_dls2_region_get_sample_info(region, &newinfo); ipatch_dls2_sample_info_notify_changes((IpatchItem *)region, &newinfo, &oldinfo); } static void ipatch_dls2_region_get_sample_info(IpatchDLS2Region *region, IpatchDLS2SampleInfo *info) { IpatchDLS2Sample *sample = NULL; gboolean info_set = TRUE; IPATCH_ITEM_RLOCK(region); if(ipatch_item_get_flags(region) & IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE && region->sample_info) { *info = *region->sample_info; } else if(region->sample) { sample = g_object_ref(region->sample); } else { info_set = FALSE; } IPATCH_ITEM_RUNLOCK(region); if(sample) { IPATCH_ITEM_RLOCK(sample); if(sample->sample_info) { *info = *sample->sample_info; } else { info_set = FALSE; } IPATCH_ITEM_RUNLOCK(sample); g_object_unref(sample); } if(!info_set) { ipatch_dls2_sample_info_init(info); } } /** * ipatch_dls2_region_get_sample: * @region: Region to get referenced sample from * * Gets the referenced sample from a region. The returned item's * reference count is incremented and the caller is responsible for * unrefing it with g_object_unref(). * * Returns: (transfer full): Region's referenced sample or %NULL if not set yet. Remember to * unreference the item with g_object_unref() when done with it. */ IpatchDLS2Sample * ipatch_dls2_region_get_sample(IpatchDLS2Region *region) { IpatchDLS2Sample *sample; g_return_val_if_fail(IPATCH_IS_DLS2_REGION(region), NULL); IPATCH_ITEM_RLOCK(region); sample = region->sample; if(sample) { g_object_ref(sample); } IPATCH_ITEM_RUNLOCK(region); return (sample); } /** * ipatch_dls2_region_peek_sample: (skip) * @region: Region to get referenced sample from * * Like ipatch_dls2_region_get_sample() but does not add a reference to * the returned item. This function should only be used if a reference * of the returned item is ensured or only the pointer value is of * interest. * * Returns: (transfer none): Region's referenced sample or %NULL if not set yet. * Remember that the item has NOT been referenced. */ IpatchDLS2Sample * ipatch_dls2_region_peek_sample(IpatchDLS2Region *region) { IpatchDLS2Sample *sample; g_return_val_if_fail(IPATCH_IS_DLS2_REGION(region), NULL); IPATCH_ITEM_RLOCK(region); sample = region->sample; IPATCH_ITEM_RUNLOCK(region); return (sample); } /** * ipatch_dls2_region_set_note_range: * @region: Region to set note range of * @low: Low value of range (MIDI note # between 0 and 127) * @high: High value of range (MIDI note # between 0 and 127) * * Set the MIDI note range that a region is active on. */ void ipatch_dls2_region_set_note_range(IpatchDLS2Region *region, int low, int high) { g_return_if_fail(IPATCH_IS_DLS2_REGION(region)); g_return_if_fail(low >= 0 && low <= 127); g_return_if_fail(high >= 0 && high <= 127); if(low > high) /* swap if backwards */ { int temp = low; low = high; high = temp; } IPATCH_ITEM_WLOCK(region); region->note_range_low = low; region->note_range_high = high; IPATCH_ITEM_WUNLOCK(region); } /** * ipatch_dls2_region_set_velocity_range: * @region: Region to set velocity range of * @low: Low value of range (MIDI velocity # between 0 and 127) * @high: High value of range (MIDI velocity # between 0 and 127) * * Set the MIDI velocity range that a region is active on. */ void ipatch_dls2_region_set_velocity_range(IpatchDLS2Region *region, int low, int high) { g_return_if_fail(IPATCH_IS_DLS2_REGION(region)); g_return_if_fail(low >= 0 && low <= 127); g_return_if_fail(high >= 0 && high <= 127); if(low > high) /* swap if backwards */ { int temp = low; low = high; high = temp; } IPATCH_ITEM_WLOCK(region); region->velocity_range_low = low; region->velocity_range_high = high; IPATCH_ITEM_WUNLOCK(region); } /** * ipatch_dls2_region_in_range: * @region: Region to check if in range * @note: MIDI note number or -1 for wildcard * @velocity: MIDI velocity or -1 for wildcard * * Check if a note and velocity falls in a region's ranges * * Returns: %TRUE if region is in note and velocity range, %FALSE otherwise */ gboolean ipatch_dls2_region_in_range(IpatchDLS2Region *region, int note, int velocity) { gboolean in_range; g_return_val_if_fail(IPATCH_IS_DLS2_REGION(region), FALSE); IPATCH_ITEM_RLOCK(region); in_range = (note == -1 || (note >= region->note_range_low && note <= region->note_range_high)) && (velocity == -1 || (velocity >= region->velocity_range_low && velocity <= region->velocity_range_high)); IPATCH_ITEM_RUNLOCK(region); return (in_range); } /** * ipatch_dls2_region_set_param: * @region: Region to set parameter of * @param: Parameter to set * @val: Value for parameter * * Sets an effect parameter of a DLS2 Region. DLS2 defines a standard set * of connections (effect parameters). Any non-standard connections can be * manipulated with the connection related functions. */ void ipatch_dls2_region_set_param(IpatchDLS2Region *region, IpatchDLS2Param param, gint32 val) { g_return_if_fail(IPATCH_IS_DLS2_REGION(region)); g_return_if_fail(param < IPATCH_DLS2_PARAM_COUNT); /* no need to lock, since write of 32 bit int is atomic */ region->params.values[param] = val; } /** * ipatch_dls2_region_set_param_array: * @region: Region to set parameter of * @array: Array of parameter values to copy to region * * Sets all effect parameters of a DLS2 Region. */ void ipatch_dls2_region_set_param_array(IpatchDLS2Region *region, IpatchDLS2ParamArray *array) { int i; g_return_if_fail(IPATCH_IS_DLS2_REGION(region)); g_return_if_fail(array != NULL); /* Write of each parameter is atomic. */ for(i = 0; i < IPATCH_DLS2_PARAM_COUNT; i++) { region->params.values[i] = array->values[i]; } } /** * ipatch_dls2_region_get_conns: * @region: Region to get connections from * * Gets a list of connections from a DLS region. List should be freed with * ipatch_dls2_conn_list_free() (free_conns set to %TRUE) when finished * with it. * * Returns: (element-type IpatchDLS2Conn) (transfer full): New list of connections * (#IpatchDLS2Conn) in @region or %NULL if no connections. Remember to free * it when finished. */ GSList * ipatch_dls2_region_get_conns(IpatchDLS2Region *region) { GSList *newlist; g_return_val_if_fail(IPATCH_IS_DLS2_REGION(region), NULL); IPATCH_ITEM_RLOCK(region); newlist = ipatch_dls2_conn_list_duplicate(region->conns); IPATCH_ITEM_RUNLOCK(region); return (newlist); } /** * ipatch_dls2_region_set_conn: * @region: DLS region * @conn: Connection * * Set a DLS connection in a region. See ipatch_dls2_conn_list_set() for * more details. */ void ipatch_dls2_region_set_conn(IpatchDLS2Region *region, const IpatchDLS2Conn *conn) { g_return_if_fail(IPATCH_IS_DLS2_REGION(region)); g_return_if_fail(conn != NULL); IPATCH_ITEM_WLOCK(region); ipatch_dls2_conn_list_set(®ion->conns, conn); IPATCH_ITEM_WUNLOCK(region); } /** * ipatch_dls2_region_unset_conn: * @region: DLS region * @conn: Connection * * Remove a DLS connection from a region. See ipatch_dls2_conn_list_unset() * for more details. */ void ipatch_dls2_region_unset_conn(IpatchDLS2Region *region, const IpatchDLS2Conn *conn) { g_return_if_fail(IPATCH_IS_DLS2_REGION(region)); g_return_if_fail(conn != NULL); IPATCH_ITEM_WLOCK(region); ipatch_dls2_conn_list_unset(®ion->conns, conn); IPATCH_ITEM_WUNLOCK(region); } /** * ipatch_dls2_region_unset_all_conns: * @region: DLS region * * Remove all connections in a region. */ void ipatch_dls2_region_unset_all_conns(IpatchDLS2Region *region) { g_return_if_fail(IPATCH_IS_DLS2_REGION(region)); IPATCH_ITEM_WLOCK(region); ipatch_dls2_conn_list_free(region->conns, TRUE); region->conns = NULL; IPATCH_ITEM_WUNLOCK(region); } /** * ipatch_dls2_region_conn_count: * @region: Region to count connections in * * Count number of connections in a region * * Returns: Count of connections */ guint ipatch_dls2_region_conn_count(IpatchDLS2Region *region) { guint i; IPATCH_ITEM_RLOCK(region); i = g_slist_length(region->conns); IPATCH_ITEM_RUNLOCK(region); return (i); } /** * ipatch_dls2_region_channel_map_stereo: * @chan: Channel steering enum * * Map a DLS2 channel steering enumeration (surround sound capable) to stereo * steering. * * Returns: -1 = left, 0 = center, 1 = right */ int ipatch_dls2_region_channel_map_stereo(IpatchDLS2RegionChannelType chan) { switch(chan) { case IPATCH_DLS2_REGION_CHANNEL_LEFT: case IPATCH_DLS2_REGION_CHANNEL_SURROUND_LEFT: case IPATCH_DLS2_REGION_CHANNEL_LEFT_OF_CENTER: case IPATCH_DLS2_REGION_CHANNEL_SIDE_LEFT: case IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_LEFT: case IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_LEFT: return (-1); case IPATCH_DLS2_REGION_CHANNEL_RIGHT: case IPATCH_DLS2_REGION_CHANNEL_SURROUND_RIGHT: case IPATCH_DLS2_REGION_CHANNEL_RIGHT_OF_CENTER: case IPATCH_DLS2_REGION_CHANNEL_SIDE_RIGHT: case IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_RIGHT: case IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_RIGHT: return (1); case IPATCH_DLS2_REGION_CHANNEL_CENTER: case IPATCH_DLS2_REGION_CHANNEL_LOW_FREQ: case IPATCH_DLS2_REGION_CHANNEL_SURROUND_CENTER: case IPATCH_DLS2_REGION_CHANNEL_TOP: case IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_CENTER: case IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_CENTER: return (0); default: return (0); } } libinstpatch-1.1.6/libinstpatch/IpatchDLS2Region.h000066400000000000000000000220041400263525300220370ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_DLS2_REGION_H__ #define __IPATCH_DLS2_REGION_H__ #include #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchDLS2Region IpatchDLS2Region; typedef struct _IpatchDLS2RegionClass IpatchDLS2RegionClass; typedef struct _IpatchDLS2ParamArray IpatchDLS2ParamArray; #define IPATCH_TYPE_DLS2_REGION (ipatch_dls2_region_get_type ()) #define IPATCH_DLS2_REGION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_DLS2_REGION, \ IpatchDLS2Region)) #define IPATCH_DLS2_REGION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_DLS2_REGION, \ IpatchDLS2RegionClass)) #define IPATCH_IS_DLS2_REGION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_DLS2_REGION)) #define IPATCH_IS_DLS2_REGION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_DLS2_REGION)) #define IPATCH_DLS2_REGION_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_DLS2_REGION, \ IpatchDLS2RegionClass)) /* standard fixed connection parameter enums */ typedef enum { IPATCH_DLS2_PARAM_MOD_LFO_FREQ, IPATCH_DLS2_PARAM_MOD_LFO_DELAY, IPATCH_DLS2_PARAM_VIB_LFO_FREQ, IPATCH_DLS2_PARAM_VIB_LFO_DELAY, IPATCH_DLS2_PARAM_VOL_EG_DELAY, IPATCH_DLS2_PARAM_VOL_EG_ATTACK, IPATCH_DLS2_PARAM_VOL_EG_HOLD, IPATCH_DLS2_PARAM_VOL_EG_DECAY, IPATCH_DLS2_PARAM_VOL_EG_SUSTAIN, IPATCH_DLS2_PARAM_VOL_EG_RELEASE, IPATCH_DLS2_PARAM_VOL_EG_SHUTDOWN, IPATCH_DLS2_PARAM_VOL_EG_VELOCITY_TO_ATTACK, IPATCH_DLS2_PARAM_VOL_EG_NOTE_TO_DECAY, IPATCH_DLS2_PARAM_VOL_EG_NOTE_TO_HOLD, IPATCH_DLS2_PARAM_MOD_EG_DELAY, IPATCH_DLS2_PARAM_MOD_EG_ATTACK, IPATCH_DLS2_PARAM_MOD_EG_HOLD, IPATCH_DLS2_PARAM_MOD_EG_DECAY, IPATCH_DLS2_PARAM_MOD_EG_SUSTAIN, IPATCH_DLS2_PARAM_MOD_EG_RELEASE, IPATCH_DLS2_PARAM_MOD_EG_VELOCITY_TO_ATTACK, IPATCH_DLS2_PARAM_MOD_EG_NOTE_TO_DECAY, IPATCH_DLS2_PARAM_MOD_EG_NOTE_TO_HOLD, IPATCH_DLS2_PARAM_SCALE_TUNE, IPATCH_DLS2_PARAM_RPN2_TO_NOTE, IPATCH_DLS2_PARAM_FILTER_CUTOFF, IPATCH_DLS2_PARAM_FILTER_Q, IPATCH_DLS2_PARAM_MOD_LFO_TO_FILTER_CUTOFF, IPATCH_DLS2_PARAM_MOD_LFO_CC1_TO_FILTER_CUTOFF, IPATCH_DLS2_PARAM_MOD_LFO_CHANNEL_PRESS_TO_FILTER_CUTOFF, IPATCH_DLS2_PARAM_MOD_EG_TO_FILTER_CUTOFF, IPATCH_DLS2_PARAM_VELOCITY_TO_FILTER_CUTOFF, IPATCH_DLS2_PARAM_NOTE_TO_FILTER_CUTOFF, IPATCH_DLS2_PARAM_MOD_LFO_TO_GAIN, IPATCH_DLS2_PARAM_MOD_LFO_CC1_TO_GAIN, IPATCH_DLS2_PARAM_MOD_LFO_CHANNEL_PRESS_TO_GAIN, IPATCH_DLS2_PARAM_VELOCITY_TO_GAIN, IPATCH_DLS2_PARAM_CC7_TO_GAIN, IPATCH_DLS2_PARAM_CC11_TO_GAIN, IPATCH_DLS2_PARAM_TUNE, IPATCH_DLS2_PARAM_PITCH_WHEEL_RPN0_TO_PITCH, IPATCH_DLS2_PARAM_NOTE_NUMBER_TO_PITCH, IPATCH_DLS2_PARAM_RPN1_TO_PITCH, IPATCH_DLS2_PARAM_VIB_LFO_TO_PITCH, IPATCH_DLS2_PARAM_VIB_LFO_CC1_TO_PITCH, IPATCH_DLS2_PARAM_VIB_LFO_CHANNEL_PRESS_TO_PITCH, IPATCH_DLS2_PARAM_MOD_LFO_TO_PITCH, IPATCH_DLS2_PARAM_MOD_LFO_CC1_TO_PITCH, IPATCH_DLS2_PARAM_MOD_LFO_CHANNEL_PRESS_TO_PITCH, IPATCH_DLS2_PARAM_MOD_EG_TO_PITCH, IPATCH_DLS2_PARAM_PAN, IPATCH_DLS2_PARAM_CC10_TO_PAN, IPATCH_DLS2_PARAM_CC91_TO_REVERB_SEND, IPATCH_DLS2_PARAM_REVERB_SEND, IPATCH_DLS2_PARAM_CC93_TO_CHORUS_SEND, IPATCH_DLS2_PARAM_CHORUS_SEND, IPATCH_DLS2_PARAM_COUNT } IpatchDLS2Param; /* DLS2 parameters array */ struct _IpatchDLS2ParamArray { gint32 values[IPATCH_DLS2_PARAM_COUNT]; }; /* DLS2 region item */ struct _IpatchDLS2Region { IpatchItem parent_instance; /*< private >*/ guint8 note_range_low; /* MIDI note range low value */ guint8 note_range_high; /* MIDI note range high value */ guint8 velocity_range_low; /* MIDI velocity range low value */ guint8 velocity_range_high; /* MIDI velocity range high value */ guint16 key_group; /* Exclusive key group number or 0 */ guint16 layer_group; /* layer group (descriptive only) */ guint16 phase_group; /* Phase locked group number or 0 */ guint16 channel; /* channel ID (IpatchDLS2RegionChannelType) */ IpatchDLS2Info *info; /* info string values */ IpatchDLS2SampleInfo *sample_info; /* sample info override or NULL */ IpatchDLS2Sample *sample; /* referenced sample */ IpatchDLS2ParamArray params; /* array of standard parameter connections */ GSList *conns; /* non-standard connections (modulators) */ }; struct _IpatchDLS2RegionClass { IpatchItemClass parent_class; }; /* channel steering enum */ typedef enum { IPATCH_DLS2_REGION_CHANNEL_LEFT = 0, IPATCH_DLS2_REGION_CHANNEL_RIGHT = 1, IPATCH_DLS2_REGION_CHANNEL_CENTER = 2, IPATCH_DLS2_REGION_CHANNEL_LOW_FREQ = 3, IPATCH_DLS2_REGION_CHANNEL_SURROUND_LEFT = 4, IPATCH_DLS2_REGION_CHANNEL_SURROUND_RIGHT = 5, IPATCH_DLS2_REGION_CHANNEL_LEFT_OF_CENTER = 6, IPATCH_DLS2_REGION_CHANNEL_RIGHT_OF_CENTER = 7, IPATCH_DLS2_REGION_CHANNEL_SURROUND_CENTER = 8, IPATCH_DLS2_REGION_CHANNEL_SIDE_LEFT = 9, IPATCH_DLS2_REGION_CHANNEL_SIDE_RIGHT = 10, IPATCH_DLS2_REGION_CHANNEL_TOP = 11, IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_LEFT = 12, IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_CENTER = 13, IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_RIGHT = 14, IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_LEFT = 15, IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_CENTER = 16, IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_RIGHT = 17 } IpatchDLS2RegionChannelType; /* mono audio alias */ #define IPATCH_DLS2_REGION_CHANNEL_MONO IPATCH_DLS2_REGION_CHANNEL_LEFT /** * IpatchDLS2RegionFlags: * @IPATCH_DLS2_REGION_SELF_NON_EXCLUSIVE: * @IPATCH_DLS2_REGION_PHASE_MASTER: * @IPATCH_DLS2_REGION_MULTI_CHANNEL: * @IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE: */ typedef enum { IPATCH_DLS2_REGION_SELF_NON_EXCLUSIVE = 1 << IPATCH_ITEM_UNUSED_FLAG_SHIFT, IPATCH_DLS2_REGION_PHASE_MASTER = 1 << (IPATCH_ITEM_UNUSED_FLAG_SHIFT + 1), IPATCH_DLS2_REGION_MULTI_CHANNEL = 1 << (IPATCH_ITEM_UNUSED_FLAG_SHIFT + 2), IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE = 1 << (IPATCH_ITEM_UNUSED_FLAG_SHIFT + 3) } IpatchDLS2RegionFlags; /** * IPATCH_DLS2_REGION_FLAG_MASK: (skip) */ #define IPATCH_DLS2_REGION_FLAG_MASK (0x0F << IPATCH_ITEM_UNUSED_FLAG_SHIFT) /** * IPATCH_DLS2_REGION_UNUSED_FLAG_SHIFT: (skip) */ /* 4 flags + 2 for expansion */ #define IPATCH_DLS2_REGION_UNUSED_FLAG_SHIFT (IPATCH_ITEM_UNUSED_FLAG_SHIFT + 6) GType ipatch_dls2_region_get_type(void); IpatchDLS2Region *ipatch_dls2_region_new(void); IpatchDLS2Region *ipatch_dls2_region_first(IpatchIter *iter); IpatchDLS2Region *ipatch_dls2_region_next(IpatchIter *iter); char *ipatch_dls2_region_get_info(IpatchDLS2Region *region, guint32 fourcc); void ipatch_dls2_region_set_info(IpatchDLS2Region *region, guint32 fourcc, const char *val); void ipatch_dls2_region_set_sample(IpatchDLS2Region *region, IpatchDLS2Sample *sample); IpatchDLS2Sample *ipatch_dls2_region_get_sample(IpatchDLS2Region *region); IpatchDLS2Sample *ipatch_dls2_region_peek_sample(IpatchDLS2Region *region); void ipatch_dls2_region_set_note_range(IpatchDLS2Region *region, int low, int high); void ipatch_dls2_region_set_velocity_range(IpatchDLS2Region *region, int low, int high); gboolean ipatch_dls2_region_in_range(IpatchDLS2Region *region, int note, int velocity); void ipatch_dls2_region_set_param(IpatchDLS2Region *region, IpatchDLS2Param param, gint32 val); void ipatch_dls2_region_set_param_array(IpatchDLS2Region *region, IpatchDLS2ParamArray *array); GSList *ipatch_dls2_region_get_conns(IpatchDLS2Region *region); void ipatch_dls2_region_set_conn(IpatchDLS2Region *region, const IpatchDLS2Conn *conn); void ipatch_dls2_region_unset_conn(IpatchDLS2Region *region, const IpatchDLS2Conn *conn); void ipatch_dls2_region_unset_all_conns(IpatchDLS2Region *region); guint ipatch_dls2_region_conn_count(IpatchDLS2Region *region); int ipatch_dls2_region_channel_map_stereo(IpatchDLS2RegionChannelType chan); #endif libinstpatch-1.1.6/libinstpatch/IpatchDLS2Sample.c000066400000000000000000000667031400263525300220460ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchDLS2Sample * @short_description: DLS audio sample object * @see_also: #IpatchDLS, #IpatchDLSRegion * @stability: Stable * * Object which defines a DLS audio sample. These objects are contained in * #IpatchDLS objects and linked (referenced) from #IpatchDLSRegion objects. */ #include #include #include #include #include "IpatchDLS2Sample.h" #include "IpatchDLS2.h" #include "IpatchDLSFile.h" #include "IpatchDLSFile_priv.h" #include "IpatchSample.h" #include "IpatchSampleStoreRam.h" #include "IpatchTypeProp.h" #include "ipatch_priv.h" #include "builtin_enums.h" /* properties */ enum { PROP_0, PROP_SAMPLE_SIZE, /* read only convenience property */ PROP_SAMPLE_FORMAT, PROP_SAMPLE_RATE, PROP_SAMPLE_DATA }; /* sample info property enums, used by regions as well, so we define these in a non-conflicting range !! Keep order synchronized with IPATCH_DLS2_SAMPLE_INFO_PROPERTY_COUNT */ enum { PROP_FLAGS = IPATCH_DLS2_SAMPLE_INFO_FIRST_PROPERTY_ID, PROP_LOOP_TYPE, PROP_ROOT_NOTE, PROP_FINE_TUNE, PROP_GAIN, PROP_LOOP_START, PROP_LOOP_END }; /* for caching sample info GParamSpec objects for an object class */ typedef struct { GObjectClass *klass; /* object class owning these properties */ GParamSpec *pspecs[IPATCH_DLS2_SAMPLE_INFO_PROPERTY_COUNT]; } ClassPropBag; static void ipatch_dls2_sample_iface_init(IpatchSampleIface *sample_iface); static gboolean ipatch_dls2_sample_iface_open(IpatchSampleHandle *handle, GError **err); static void ipatch_dls2_sample_finalize(GObject *gobject); static void ipatch_dls2_sample_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_dls2_sample_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_dls2_sample_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static void ipatch_dls2_sample_item_remove_full(IpatchItem *item, gboolean full); static gboolean ipatch_dls2_sample_real_set_data(IpatchDLS2Sample *sample, IpatchSampleData *sampledata); /* list of ClassPropBag to speed up info property notifies */ static GSList *info_pspec_list = NULL; G_DEFINE_TYPE_WITH_CODE(IpatchDLS2Sample, ipatch_dls2_sample, IPATCH_TYPE_ITEM, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_dls2_sample_iface_init)) /* ----- Initialization/deinitialization of ClassPropBag list ---------------*/ void _ipatch_DLS2_sample_init(void) { info_pspec_list = NULL; } void _ipatch_DLS2_sample_deinit() { g_slist_free_full(info_pspec_list, g_free); } /* ----- IpatchDSL2SAmple object functions ---------------------------------*/ /* sample interface initialization */ static void ipatch_dls2_sample_iface_init(IpatchSampleIface *sample_iface) { sample_iface->open = ipatch_dls2_sample_iface_open; sample_iface->loop_types = ipatch_sample_loop_types_standard_release; } static gboolean ipatch_dls2_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchDLS2Sample *sample = IPATCH_DLS2_SAMPLE(handle->sample); g_return_val_if_fail(sample->sample_data != NULL, FALSE); return (ipatch_sample_handle_cascade_open (handle, (IpatchSample *)(sample->sample_data), err)); } static void ipatch_dls2_sample_class_init(IpatchDLS2SampleClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->finalize = ipatch_dls2_sample_finalize; obj_class->get_property = ipatch_dls2_sample_get_property; /* we use the IpatchItem item_set_property method */ item_class->item_set_property = ipatch_dls2_sample_set_property; item_class->copy = ipatch_dls2_sample_item_copy; item_class->remove_full = ipatch_dls2_sample_item_remove_full; g_object_class_override_property(obj_class, IPATCH_DLS2_NAME, "title"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_SIZE, "sample-size"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_FORMAT, "sample-format"); ipatch_sample_install_property(obj_class, PROP_SAMPLE_RATE, "sample-rate"); ipatch_sample_install_property(obj_class, PROP_SAMPLE_DATA, "sample-data"); ipatch_dls2_sample_info_install_class_properties(obj_class); ipatch_dls2_info_install_class_properties(obj_class); } static void ipatch_dls2_sample_init(IpatchDLS2Sample *sample) { ipatch_dls2_sample_set_blank(sample); sample->rate = IPATCH_SAMPLE_RATE_DEFAULT; } static void ipatch_dls2_sample_finalize(GObject *gobject) { IpatchDLS2Sample *sample = IPATCH_DLS2_SAMPLE(gobject); /* nothing should reference the sample after this, but we set pointers to NULL to help catch invalid references. Locking of sample is required since in reality all its children do still hold references */ IPATCH_ITEM_WLOCK(sample); if(sample->sample_data) { ipatch_sample_data_unused(sample->sample_data); // -- dec use count g_object_unref(sample->sample_data); // -- dec reference count } if(sample->sample_info) { ipatch_dls2_sample_info_free(sample->sample_info); } ipatch_dls2_info_free(sample->info); g_free(sample->dlid); IPATCH_ITEM_WUNLOCK(sample); if(G_OBJECT_CLASS(ipatch_dls2_sample_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_dls2_sample_parent_class)->finalize(gobject); } } static void ipatch_dls2_sample_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchDLS2Sample *sample = IPATCH_DLS2_SAMPLE(object); gboolean retval; switch(property_id) { case PROP_SAMPLE_RATE: IPATCH_ITEM_WLOCK(sample); sample->rate = g_value_get_int(value); IPATCH_ITEM_WUNLOCK(sample); break; case PROP_SAMPLE_DATA: ipatch_dls2_sample_real_set_data(sample, (IpatchSampleData *) (g_value_get_object(value))); break; default: IPATCH_ITEM_WLOCK(sample); retval = ipatch_dls2_sample_info_set_property(&sample->sample_info, property_id, value); if(!retval) retval = ipatch_dls2_info_set_property(&sample->info, property_id, value); IPATCH_ITEM_WUNLOCK(sample); /* check if "title" property needs to be notified */ if(property_id == IPATCH_DLS2_NAME) ipatch_item_prop_notify((IpatchItem *)sample, ipatch_item_pspec_title, value, NULL); if(!retval) { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } break; } } static void ipatch_dls2_sample_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchDLS2Sample *sample = IPATCH_DLS2_SAMPLE(object); gboolean retval; switch(property_id) { case PROP_SAMPLE_SIZE: g_return_if_fail(sample->sample_data != NULL); g_object_get_property((GObject *)(sample->sample_data), "sample-size", value); break; case PROP_SAMPLE_FORMAT: g_return_if_fail(sample->sample_data != NULL); g_object_get_property((GObject *)(sample->sample_data), "sample-format", value); break; case PROP_SAMPLE_RATE: IPATCH_ITEM_RLOCK(sample); g_value_set_int(value, sample->rate); IPATCH_ITEM_RUNLOCK(sample); break; case PROP_SAMPLE_DATA: g_value_take_object(value, ipatch_dls2_sample_get_data(sample)); break; default: IPATCH_ITEM_RLOCK(sample); retval = ipatch_dls2_sample_info_get_property(sample->sample_info, property_id, value); if(!retval) retval = ipatch_dls2_info_get_property(sample->info, property_id, value); IPATCH_ITEM_RUNLOCK(sample); if(!retval) { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } break; } } static void ipatch_dls2_sample_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchDLS2Sample *src_sam, *dest_sam; src_sam = IPATCH_DLS2_SAMPLE(src); dest_sam = IPATCH_DLS2_SAMPLE(dest); IPATCH_ITEM_RLOCK(src_sam); dest_sam->sample_info = src_sam->sample_info ? ipatch_dls2_sample_info_duplicate(src_sam->sample_info) : NULL; dest_sam->info = ipatch_dls2_info_duplicate(src_sam->info); ipatch_dls2_sample_set_data(dest_sam, src_sam->sample_data); if(src_sam->dlid) { dest_sam->dlid = g_memdup(src_sam->dlid, IPATCH_DLS_DLID_SIZE); } IPATCH_ITEM_RUNLOCK(src_sam); } static void ipatch_dls2_sample_item_remove_full(IpatchItem *item, gboolean full) { IpatchList *list; IpatchIter iter; IpatchItem *region; /* ++ ref new list */ list = ipatch_dls2_get_region_references(IPATCH_DLS2_SAMPLE(item)); ipatch_list_init_iter(list, &iter); region = ipatch_item_first(&iter); while(region) { ipatch_item_remove(region); item = ipatch_item_next(&iter); } g_object_unref(list); /* -- unref list */ if(full) { ipatch_dls2_sample_set_data(IPATCH_DLS2_SAMPLE(item), NULL); } if(IPATCH_ITEM_CLASS(ipatch_dls2_sample_parent_class)->remove_full) { IPATCH_ITEM_CLASS(ipatch_dls2_sample_parent_class)->remove_full(item, full); } } /** * ipatch_dls2_sample_new: * * Create a new DLS sample object. * * Returns: New DLS sample with a reference count of 1. Caller * owns the reference and removing it will destroy the item, unless another * reference is added (if its parented for example). */ IpatchDLS2Sample * ipatch_dls2_sample_new(void) { return (IPATCH_DLS2_SAMPLE(g_object_new(IPATCH_TYPE_DLS2_SAMPLE, NULL))); } /** * ipatch_dls2_sample_first: (skip) * @iter: Patch item iterator containing #IpatchDLS2Sample items * * Gets the first item in a sample iterator. A convenience wrapper for * ipatch_iter_first(). * * Returns: The first sample in @iter or %NULL if empty. */ IpatchDLS2Sample * ipatch_dls2_sample_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_DLS2_SAMPLE(obj)); } else { return (NULL); } } /** * ipatch_dls2_sample_next: (skip) * @iter: Patch item iterator containing #IpatchDLS2Sample items * * Gets the next item in a sample iterator. A convenience wrapper for * ipatch_iter_next(). * * Returns: The next sample in @iter or %NULL if at the end of the list. */ IpatchDLS2Sample * ipatch_dls2_sample_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_DLS2_SAMPLE(obj)); } else { return (NULL); } } /** * ipatch_dls2_sample_set_data: * @sample: Sample to set sample data of * @sampledata: Sample data to set sample to. Should be NULL or a IpatchSampleData object * * Set a sample's sample data object. */ void ipatch_dls2_sample_set_data(IpatchDLS2Sample *sample, IpatchSampleData *sampledata) { if(ipatch_dls2_sample_real_set_data(sample, sampledata)) { g_object_notify(G_OBJECT(sample), "sample-data"); } } /* the actual setting of sample data, user routine does a g_object_notify */ static gboolean ipatch_dls2_sample_real_set_data(IpatchDLS2Sample *sample, IpatchSampleData *sampledata) { IpatchSampleData *old_sampledata; g_return_val_if_fail(IPATCH_IS_DLS2_SAMPLE(sample), FALSE); if(sampledata != NULL) { g_return_val_if_fail (IPATCH_IS_SAMPLE_DATA (sampledata), FALSE); g_object_ref (sampledata); /* ++ ref for sample */ ipatch_sample_data_used (sampledata); /* ++ inc use count */ } IPATCH_ITEM_WLOCK(sample); old_sampledata = sample->sample_data; sample->sample_data = sampledata; /* !! takes over ref */ IPATCH_ITEM_WUNLOCK(sample); if(old_sampledata) { ipatch_sample_data_unused(old_sampledata); // -- dec use count g_object_unref(old_sampledata); // -- dec reference count } return (TRUE); } /** * ipatch_dls2_sample_get_data: * @sample: Sample to get sample data from * * Get the #IpatchSampleData item of a sample. Sample data item is referenced * before returning and caller is responsible for unreferencing it with * g_object_unref() when finished with it. * * Returns: (transfer full): Sample data object of sample or %NULL if none. Remember to * unreference with g_object_unref() when finished with it. */ IpatchSampleData * ipatch_dls2_sample_get_data(IpatchDLS2Sample *sample) { IpatchSampleData *sampledata; g_return_val_if_fail(IPATCH_IS_DLS2_SAMPLE(sample), NULL); IPATCH_ITEM_RLOCK(sample); sampledata = sample->sample_data; if(sampledata) { g_object_ref(sampledata); /* ++ ref */ } IPATCH_ITEM_RUNLOCK(sample); return (sampledata); /* !! caller takes over ref */ } /** * ipatch_dls2_sample_peek_data: (skip) * @sample: Sample to get sample data from * * Get the #IpatchSampleData item of a sample. Like * ipatch_dls2_sample_get_data() but sample data object is not referenced. * This function should only be used if a reference of the sample data object * is ensured or only the pointer value is of importance. * * Returns: (transfer none): Sample data object of sample or %NULL if none. * Remember that a reference is NOT added. */ IpatchSampleData * ipatch_dls2_sample_peek_data(IpatchDLS2Sample *sample) { IpatchSampleData *sampledata; g_return_val_if_fail(IPATCH_IS_DLS2_SAMPLE(sample), NULL); IPATCH_ITEM_RLOCK(sample); sampledata = sample->sample_data; IPATCH_ITEM_RUNLOCK(sample); return (sampledata); } /** * ipatch_dls2_sample_set_blank: * @sample: Sample to set to blank sample data * * Set the sample data of a sample item to blank data. */ void ipatch_dls2_sample_set_blank(IpatchDLS2Sample *sample) { IpatchSampleData *sampledata; g_return_if_fail(IPATCH_IS_DLS2_SAMPLE(sample)); sampledata = ipatch_sample_data_get_blank(); IPATCH_ITEM_WLOCK(sample); if(sample->sample_info) /* reset sample info to defaults */ { ipatch_dls2_sample_info_free(sample->sample_info); sample->sample_info = NULL; } g_object_set(sample, "sample-data", sampledata, "sample-rate", 44100, NULL); IPATCH_ITEM_WUNLOCK(sample); g_object_unref(sampledata); } GType ipatch_dls2_sample_info_get_type(void) { static GType type = 0; if(!type) type = g_boxed_type_register_static("IpatchDLS2SampleInfo", (GBoxedCopyFunc)ipatch_dls2_sample_info_duplicate, (GBoxedFreeFunc)ipatch_dls2_sample_info_free); return (type); } /** * ipatch_dls2_sample_info_new: * * Allocates a new sample info structure. * * Returns: (transfer full): New sample info structure, free it with * ipatch_dls2_sample_info_free() when finished. */ IpatchDLS2SampleInfo * ipatch_dls2_sample_info_new(void) { IpatchDLS2SampleInfo *sample_info; sample_info = g_slice_new0(IpatchDLS2SampleInfo); sample_info->root_note = 60; return (sample_info); } /** * ipatch_dls2_sample_info_free: * @sample_info: Sample info structure * * Free a sample info structure allocated with ipatch_dls2_sample_info_new(). */ void ipatch_dls2_sample_info_free(IpatchDLS2SampleInfo *sample_info) { g_slice_free(IpatchDLS2SampleInfo, sample_info); } /** * ipatch_dls2_sample_info_duplicate: * @sample_info: Sample info structure to duplicate * * Duplicate a sample info structure. * * Returns: Newly allocated sample info structure which should be freed * with ipatch_dls2_sample_info_free() when done with it. */ IpatchDLS2SampleInfo * ipatch_dls2_sample_info_duplicate(IpatchDLS2SampleInfo *sample_info) { IpatchDLS2SampleInfo *newinfo; g_return_val_if_fail(sample_info != NULL, NULL); newinfo = ipatch_dls2_sample_info_new(); *newinfo = *sample_info; return (newinfo); } /** * ipatch_dls2_sample_info_init: * @sample_info: Sample info structure to initialize * * Initialize a sample info structure to defaults. */ void ipatch_dls2_sample_info_init(IpatchDLS2SampleInfo *sample_info) { g_return_if_fail(sample_info != NULL); memset(sample_info, 0, sizeof(IpatchDLS2SampleInfo)); sample_info->root_note = 60; } /** * ipatch_dls2_sample_info_install_class_properties: (skip) * @obj_class: GObjectClass to install properties for * * Installs sample info properties for the given @obj_class. Useful for * objects that implement #IpatchDLS2SampleInfo properties. */ void ipatch_dls2_sample_info_install_class_properties(GObjectClass *obj_class) { ClassPropBag *bag; /* add new bag to cache pspecs for this class */ bag = g_new(ClassPropBag, 1); bag->klass = obj_class; info_pspec_list = g_slist_append(info_pspec_list, bag); /* properties defined by IpatchSample interface */ bag->pspecs[0] = ipatch_sample_install_property(obj_class, PROP_LOOP_TYPE, "loop-type"); bag->pspecs[1] = ipatch_sample_install_property(obj_class, PROP_LOOP_START, "loop-start"); bag->pspecs[2] = ipatch_sample_install_property(obj_class, PROP_LOOP_END, "loop-end"); bag->pspecs[3] = ipatch_sample_install_property(obj_class, PROP_ROOT_NOTE, "root-note"); bag->pspecs[4] = ipatch_sample_install_property(obj_class, PROP_FINE_TUNE, "fine-tune"); bag->pspecs[5] = g_param_spec_flags("flags", _("Sample flags"), _("Sample flags"), IPATCH_TYPE_DLS2_SAMPLE_FLAGS, 0, G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_FLAGS, bag->pspecs[5]); bag->pspecs[6] = g_param_spec_int("gain", _("Gain"), _("Gain in DLS relative gain units"), G_MININT, G_MAXINT, 0, G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_GAIN, bag->pspecs[6]); } /** * ipatch_dls2_sample_info_is_property_id_valid: (skip) * @property_id: Property ID to test * * Check if a property ID is a valid sample info property ID. * * Returns: %TRUE if property_id is a sample info property ID, %FALSE otherwise. */ gboolean ipatch_dls2_sample_info_is_property_id_valid(guint property_id) { return (property_id == PROP_FLAGS || property_id == PROP_LOOP_TYPE || property_id == PROP_ROOT_NOTE || property_id == PROP_FINE_TUNE || property_id == PROP_GAIN || property_id == PROP_LOOP_START || property_id == PROP_LOOP_END); } /** * ipatch_dls2_sample_info_set_property: (skip) * @sample_info: Pointer to pointer to sample info * @property_id: Property ID * @value: Value for property * * A function used by set_property methods that implement #IpatchDLS2SampleInfo * properties. * * Returns: %TRUE if property_id was handled, %FALSE otherwise */ gboolean ipatch_dls2_sample_info_set_property(IpatchDLS2SampleInfo **sample_info, guint property_id, const GValue *value) { IpatchDLS2SampleInfo *saminfo; if(!*sample_info) { if(property_id != PROP_FLAGS && property_id != PROP_LOOP_TYPE && property_id != PROP_ROOT_NOTE && property_id != PROP_FINE_TUNE && property_id != PROP_GAIN && property_id != PROP_LOOP_START && property_id != PROP_LOOP_END) { return (FALSE); } *sample_info = ipatch_dls2_sample_info_new(); } saminfo = *sample_info; switch(property_id) { case PROP_FLAGS: saminfo->options &= ~IPATCH_DLS2_SAMPLE_FLAGS_MASK; saminfo->options |= g_value_get_flags(value) & IPATCH_DLS2_SAMPLE_FLAGS_MASK; break; case PROP_LOOP_TYPE: saminfo->options &= ~IPATCH_DLS2_SAMPLE_LOOP_MASK; saminfo->options |= g_value_get_enum(value) & IPATCH_DLS2_SAMPLE_LOOP_MASK; break; case PROP_ROOT_NOTE: saminfo->root_note = g_value_get_int(value); break; case PROP_FINE_TUNE: saminfo->fine_tune = g_value_get_int(value); break; case PROP_GAIN: saminfo->gain = g_value_get_int(value); break; case PROP_LOOP_START: saminfo->loop_start = g_value_get_uint(value); break; case PROP_LOOP_END: saminfo->loop_end = g_value_get_uint(value); break; default: return (FALSE); } return (TRUE); } /** * ipatch_dls2_sample_info_get_property: (skip) * @sample_info: Pointer to sample info * @property_id: Property ID * @value: Value to set * * A function used by get_property methods that implement #IpatchDLS2SampleInfo * properties. * * Returns: %TRUE if property_id was handled, %FALSE otherwise */ gboolean ipatch_dls2_sample_info_get_property(IpatchDLS2SampleInfo *sample_info, guint property_id, GValue *value) { switch(property_id) { case PROP_FLAGS: g_value_set_flags(value, sample_info ? (sample_info->options & IPATCH_DLS2_SAMPLE_FLAGS_MASK) : 0); break; case PROP_LOOP_TYPE: g_value_set_enum(value, sample_info ? (sample_info->options & IPATCH_DLS2_SAMPLE_LOOP_MASK) : IPATCH_SAMPLE_LOOP_NONE); break; case PROP_ROOT_NOTE: g_value_set_int(value, sample_info ? sample_info->root_note : 60); break; case PROP_FINE_TUNE: g_value_set_int(value, sample_info ? sample_info->fine_tune : 0); break; case PROP_GAIN: g_value_set_int(value, sample_info ? sample_info->gain : 0); break; case PROP_LOOP_START: g_value_set_uint(value, sample_info ? sample_info->loop_start : 0); break; case PROP_LOOP_END: g_value_set_uint(value, sample_info ? sample_info->loop_end : 0); break; default: return (FALSE); } return (TRUE); } /** * ipatch_dls2_sample_info_notify_changes: (skip) * @item: Item to send #IpatchItem property notifies on * @newinfo: New sample info values * @oldinfo: Old sample info values * * Sends #IpatchItem property notifies for changed sample info parameters. */ void ipatch_dls2_sample_info_notify_changes(IpatchItem *item, IpatchDLS2SampleInfo *newinfo, IpatchDLS2SampleInfo *oldinfo) { GParamSpec **found_pspec_cache = NULL; GObjectClass *klass; GValue newval = { 0 }, oldval = { 0 }; GSList *p; g_return_if_fail(IPATCH_IS_ITEM(item)); klass = G_OBJECT_GET_CLASS(item); /* search for param spec cache for object's class */ for(p = info_pspec_list; p; p = p->next) { if(((ClassPropBag *)(p->data))->klass == klass) { found_pspec_cache = ((ClassPropBag *)(p->data))->pspecs; break; } } g_return_if_fail(found_pspec_cache); if((oldinfo->options & IPATCH_DLS2_SAMPLE_LOOP_MASK) != (newinfo->options & IPATCH_DLS2_SAMPLE_LOOP_MASK)) { g_value_init(&newval, IPATCH_TYPE_SAMPLE_LOOP_TYPE); g_value_init(&oldval, IPATCH_TYPE_SAMPLE_LOOP_TYPE); g_value_set_enum(&newval, newinfo->options & IPATCH_DLS2_SAMPLE_LOOP_MASK); g_value_set_enum(&oldval, oldinfo->options & IPATCH_DLS2_SAMPLE_LOOP_MASK); ipatch_item_prop_notify(item, found_pspec_cache[0], &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } if((oldinfo->options & IPATCH_DLS2_SAMPLE_FLAGS_MASK) != (newinfo->options & IPATCH_DLS2_SAMPLE_FLAGS_MASK)) { g_value_init(&newval, IPATCH_TYPE_DLS2_SAMPLE_FLAGS); g_value_init(&oldval, IPATCH_TYPE_DLS2_SAMPLE_FLAGS); g_value_set_flags(&newval, newinfo->options & IPATCH_DLS2_SAMPLE_FLAGS_MASK); g_value_set_flags(&oldval, oldinfo->options & IPATCH_DLS2_SAMPLE_FLAGS_MASK); ipatch_item_prop_notify(item, found_pspec_cache[1], &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } if(oldinfo->root_note != newinfo->root_note) { g_value_init(&newval, G_TYPE_INT); g_value_init(&oldval, G_TYPE_INT); g_value_set_int(&newval, newinfo->root_note); g_value_set_int(&oldval, oldinfo->root_note); ipatch_item_prop_notify(item, found_pspec_cache[2], &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } if(oldinfo->fine_tune != newinfo->fine_tune) { g_value_init(&newval, G_TYPE_INT); g_value_init(&oldval, G_TYPE_INT); g_value_set_int(&newval, newinfo->fine_tune); g_value_set_int(&oldval, oldinfo->fine_tune); ipatch_item_prop_notify(item, found_pspec_cache[3], &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } if(oldinfo->gain != newinfo->gain) { g_value_init(&newval, G_TYPE_INT); g_value_init(&oldval, G_TYPE_INT); g_value_set_int(&newval, newinfo->gain); g_value_set_int(&oldval, oldinfo->gain); ipatch_item_prop_notify(item, found_pspec_cache[4], &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } if(oldinfo->loop_start != newinfo->loop_start) { g_value_init(&newval, G_TYPE_UINT); g_value_init(&oldval, G_TYPE_UINT); g_value_set_uint(&newval, newinfo->loop_start); g_value_set_uint(&oldval, oldinfo->loop_start); ipatch_item_prop_notify(item, found_pspec_cache[5], &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } if(oldinfo->loop_end != newinfo->loop_end) { g_value_init(&newval, G_TYPE_UINT); g_value_init(&oldval, G_TYPE_UINT); g_value_set_uint(&newval, newinfo->loop_end); g_value_set_uint(&oldval, oldinfo->loop_end); ipatch_item_prop_notify(item, found_pspec_cache[6], &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } } libinstpatch-1.1.6/libinstpatch/IpatchDLS2Sample.h000066400000000000000000000120341400263525300220370ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_DLS2_SAMPLE_H__ #define __IPATCH_DLS2_SAMPLE_H__ #include #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchDLS2Sample IpatchDLS2Sample; typedef struct _IpatchDLS2SampleClass IpatchDLS2SampleClass; typedef struct _IpatchDLS2SampleInfo IpatchDLS2SampleInfo; #define IPATCH_TYPE_DLS2_SAMPLE (ipatch_dls2_sample_get_type ()) #define IPATCH_DLS2_SAMPLE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_DLS2_SAMPLE, \ IpatchDLS2Sample)) #define IPATCH_DLS2_SAMPLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_DLS2_SAMPLE, \ IpatchDLS2SampleClass)) #define IPATCH_IS_DLS2_SAMPLE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_DLS2_SAMPLE)) #define IPATCH_IS_DLS2_SAMPLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_DLS2_SAMPLE)) #define IPATCH_DLS2_SAMPLE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_DLS2_SAMPLE, \ IpatchDLS2SampleClass)) #define IPATCH_TYPE_DLS2_SAMPLE_INFO (ipatch_dls2_sample_info_get_type ()) /* SoundFont sample item */ struct _IpatchDLS2Sample { IpatchItem parent_instance; int rate; /* sample rate */ IpatchDLS2SampleInfo *sample_info; /* sample data info (optional) */ IpatchDLS2Info *info; /* info string values */ IpatchSampleData *sample_data; /* sample data object */ guint8 *dlid; /* 16 byte unique ID or NULL */ }; struct _IpatchDLS2SampleClass { IpatchItemClass parent_class; }; /* DLS2 sample info, can also be defined in regions */ struct _IpatchDLS2SampleInfo { guint8 options; /* IpatchSampleLoopType and IpatchDLS2SampleFlags */ guint8 root_note; /* root MIDI note number */ gint16 fine_tune; /* fine tuning */ gint32 gain; /* gain to apply to sample */ guint32 loop_start; /* loop start offset (in samples) */ guint32 loop_end; /* loop end offset (in samples) */ }; /* default values to initialize static IpatchDLS2SampleInfo with */ #define IPATCH_DLS2_SAMPLE_INFO_INIT { 0, 60, 0, 0, 0, 0 } /** * IPATCH_DLS2_SAMPLE_INFO_FIRST_PROPERTY_ID: (skip) */ /* since sample info is also used by regions, we define a non-conflicting property ID here for the first sample info property */ #define IPATCH_DLS2_SAMPLE_INFO_FIRST_PROPERTY_ID 256 /** * IPATCH_DLS2_SAMPLE_INFO_PROPERTY_COUNT: (skip) */ /* count of sample info properties */ #define IPATCH_DLS2_SAMPLE_INFO_PROPERTY_COUNT 7 /* flags crammed into sample info options field */ typedef enum { IPATCH_DLS2_SAMPLE_NO_TRUNCATION = 1 << 6, IPATCH_DLS2_SAMPLE_NO_COMPRESSION = 1 << 7 } IpatchDLS2SampleFlags; #define IPATCH_DLS2_SAMPLE_LOOP_MASK 0x03 #define IPATCH_DLS2_SAMPLE_FLAGS_MASK 0x0C0 GType ipatch_dls2_sample_get_type(void); IpatchDLS2Sample *ipatch_dls2_sample_new(void); IpatchDLS2Sample *ipatch_dls2_sample_first(IpatchIter *iter); IpatchDLS2Sample *ipatch_dls2_sample_next(IpatchIter *iter); void ipatch_dls2_sample_set_data(IpatchDLS2Sample *sample, IpatchSampleData *sampledata); IpatchSampleData *ipatch_dls2_sample_get_data(IpatchDLS2Sample *sample); IpatchSampleData *ipatch_dls2_sample_peek_data(IpatchDLS2Sample *sample); void ipatch_dls2_sample_set_blank(IpatchDLS2Sample *sample); GType ipatch_dls2_sample_info_get_type(void); IpatchDLS2SampleInfo *ipatch_dls2_sample_info_new(void); void ipatch_dls2_sample_info_free(IpatchDLS2SampleInfo *sample_info); IpatchDLS2SampleInfo *ipatch_dls2_sample_info_duplicate (IpatchDLS2SampleInfo *sample_info); void ipatch_dls2_sample_info_init(IpatchDLS2SampleInfo *sample_info); void ipatch_dls2_sample_info_install_class_properties (GObjectClass *obj_class); gboolean ipatch_dls2_sample_info_is_property_id_valid(guint property_id); gboolean ipatch_dls2_sample_info_set_property (IpatchDLS2SampleInfo **sample_info, guint property_id, const GValue *value); gboolean ipatch_dls2_sample_info_get_property (IpatchDLS2SampleInfo *sample_info, guint property_id, GValue *value); void ipatch_dls2_sample_info_notify_changes(IpatchItem *item, IpatchDLS2SampleInfo *newinfo, IpatchDLS2SampleInfo *oldinfo); #endif libinstpatch-1.1.6/libinstpatch/IpatchDLSFile.c000066400000000000000000000063561400263525300214200ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchDLSFile * @short_description: DLS file object and functions * @see_also: * @stability: Stable * * Object type for DLS files and other constants and functions dealing with * them. */ #include #include #include #include #include "IpatchDLSFile.h" #include "IpatchDLSFile_priv.h" #include "ipatch_priv.h" #include "i18n.h" #include "misc.h" static gboolean ipatch_dls_file_identify(IpatchFile *file, IpatchFileHandle *handle, GError **err); G_DEFINE_TYPE(IpatchDLSFile, ipatch_dls_file, IPATCH_TYPE_FILE) static void ipatch_dls_file_class_init(IpatchDLSFileClass *klass) { IpatchFileClass *file_class = IPATCH_FILE_CLASS(klass); file_class->identify = ipatch_dls_file_identify; } static void ipatch_dls_file_init(IpatchDLSFile *file) { } /* DLS/gig file identification function */ static gboolean ipatch_dls_file_identify(IpatchFile *file, IpatchFileHandle *handle, GError **err) { guint32 buf[3]; char *filename; gboolean retval = FALSE; int len = 0; /* Silence gcc */ filename = ipatch_file_get_name(file); /* ++ alloc file name */ /* Check if file ends with .gig extension first, since we can't easily * check by content. Defer to gig identify method. */ if(filename) { len = strlen(filename); if(len >= 4 && g_ascii_strcasecmp(filename + len - 4, ".gig") == 0) { g_free(filename); /* -- free file name */ return (FALSE); } } if(handle) /* Test content */ { if(ipatch_file_read(handle, buf, 12, err) && buf[0] == IPATCH_FOURCC_RIFF && buf[2] == IPATCH_DLS_FOURCC_DLS) { retval = TRUE; } } else if(filename) /* Test file name extension */ { if((len >= 4 && g_ascii_strcasecmp(filename + len - 4, ".dls") == 0) || (len >= 5 && g_ascii_strcasecmp(filename + len - 5, ".dls2") == 0)) { retval = TRUE; } } g_free(filename); /* -- free filename */ return (retval); } /** * ipatch_dls_file_new: * * Create a new DLS file object. * * Returns: New DLS file object (derived from IpatchFile) with a * reference count of 1. Caller owns the reference and removing it will * destroy the item. */ IpatchDLSFile * ipatch_dls_file_new(void) { return (IPATCH_DLS_FILE(g_object_new(IPATCH_TYPE_DLS_FILE, NULL))); } libinstpatch-1.1.6/libinstpatch/IpatchDLSFile.h000066400000000000000000000063551400263525300214240ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_DLS_FILE_H__ #define __IPATCH_DLS_FILE_H__ #include #include #include #include typedef struct _IpatchDLSFile IpatchDLSFile; typedef struct _IpatchDLSFileClass IpatchDLSFileClass; #define IPATCH_TYPE_DLS_FILE (ipatch_dls_file_get_type ()) #define IPATCH_DLS_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_DLS_FILE, IpatchDLSFile)) #define IPATCH_DLS_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_DLS_FILE, \ IpatchDLSFileClass)) #define IPATCH_IS_DLS_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_DLS_FILE)) #define IPATCH_IS_DLS_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_DLS_FILE)) #define IPATCH_DLS_FILE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_DLS_FILE, \ IpatchDLSFileClass)) /* INFO FOURCC ids (user friendly defines in IpatchDLS2Info.h) */ #define IPATCH_DLS_FOURCC_IARL IPATCH_FOURCC ('I','A','R','L') #define IPATCH_DLS_FOURCC_IART IPATCH_FOURCC ('I','A','R','T') #define IPATCH_DLS_FOURCC_ICMS IPATCH_FOURCC ('I','C','M','S') #define IPATCH_DLS_FOURCC_ICMT IPATCH_FOURCC ('I','C','M','T') #define IPATCH_DLS_FOURCC_ICOP IPATCH_FOURCC ('I','C','O','P') #define IPATCH_DLS_FOURCC_ICRD IPATCH_FOURCC ('I','C','R','D') #define IPATCH_DLS_FOURCC_IENG IPATCH_FOURCC ('I','E','N','G') #define IPATCH_DLS_FOURCC_IGNR IPATCH_FOURCC ('I','G','N','R') #define IPATCH_DLS_FOURCC_IKEY IPATCH_FOURCC ('I','K','E','Y') #define IPATCH_DLS_FOURCC_IMED IPATCH_FOURCC ('I','M','E','D') #define IPATCH_DLS_FOURCC_INAM IPATCH_FOURCC ('I','N','A','M') #define IPATCH_DLS_FOURCC_IPRD IPATCH_FOURCC ('I','P','R','D') #define IPATCH_DLS_FOURCC_ISBJ IPATCH_FOURCC ('I','S','B','J') #define IPATCH_DLS_FOURCC_ISFT IPATCH_FOURCC ('I','S','F','T') #define IPATCH_DLS_FOURCC_ISRC IPATCH_FOURCC ('I','S','R','C') #define IPATCH_DLS_FOURCC_ISRF IPATCH_FOURCC ('I','S','R','F') #define IPATCH_DLS_FOURCC_ITCH IPATCH_FOURCC ('I','T','C','H') /** * IPATCH_DLS_DLID_SIZE: (skip) */ #define IPATCH_DLS_DLID_SIZE 16 /* DLID unique ID chunk size */ /* DLS file object (derived from IpatchFile) */ struct _IpatchDLSFile { IpatchFile parent_instance; }; /* DLS file class (derived from IpatchFile) */ struct _IpatchDLSFileClass { IpatchFileClass parent_class; }; GType ipatch_dls_file_get_type(void); IpatchDLSFile *ipatch_dls_file_new(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchDLSFile_priv.h000066400000000000000000000074731400263525300224660ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_DLS_FILE_PRIV_H__ #define __IPATCH_DLS_FILE_PRIV_H__ /* RIFF chunk FOURCC guint32 integers */ #define IPATCH_DLS_FOURCC_DLS IPATCH_FOURCC ('D','L','S',' ') #define IPATCH_DLS_FOURCC_COLH IPATCH_FOURCC ('c','o','l','h') #define IPATCH_DLS_FOURCC_WVPL IPATCH_FOURCC ('w','v','p','l') #define IPATCH_DLS_FOURCC_DWPL IPATCH_FOURCC ('d','w','p','l') #define IPATCH_DLS_FOURCC_PTBL IPATCH_FOURCC ('p','t','b','l') #define IPATCH_DLS_FOURCC_PATH IPATCH_FOURCC ('p','a','t','h') #define IPATCH_DLS_FOURCC_WAVE IPATCH_FOURCC ('w','a','v','e') #define IPATCH_DLS_FOURCC_LINS IPATCH_FOURCC ('l','i','n','s') #define IPATCH_DLS_FOURCC_INS IPATCH_FOURCC ('i','n','s',' ') #define IPATCH_DLS_FOURCC_INSH IPATCH_FOURCC ('i','n','s','h') #define IPATCH_DLS_FOURCC_LRGN IPATCH_FOURCC ('l','r','g','n') #define IPATCH_DLS_FOURCC_RGN IPATCH_FOURCC ('r','g','n',' ') #define IPATCH_DLS_FOURCC_RGNH IPATCH_FOURCC ('r','g','n','h') #define IPATCH_DLS_FOURCC_LART IPATCH_FOURCC ('l','a','r','t') #define IPATCH_DLS_FOURCC_ART1 IPATCH_FOURCC ('a','r','t','1') #define IPATCH_DLS_FOURCC_WLNK IPATCH_FOURCC ('w','l','n','k') #define IPATCH_DLS_FOURCC_WSMP IPATCH_FOURCC ('w','s','m','p') #define IPATCH_DLS_FOURCC_VERS IPATCH_FOURCC ('v','e','r','s') #define IPATCH_DLS_FOURCC_RGN2 IPATCH_FOURCC ('r','g','n','2') #define IPATCH_DLS_FOURCC_LAR2 IPATCH_FOURCC ('l','a','r','2') #define IPATCH_DLS_FOURCC_ART2 IPATCH_FOURCC ('a','r','t','2') #define IPATCH_DLS_FOURCC_CDL IPATCH_FOURCC ('c','d','l',' ') #define IPATCH_DLS_FOURCC_DLID IPATCH_FOURCC ('d','l','i','d') #define IPATCH_DLS_FOURCC_INFO IPATCH_FOURCC ('I','N','F','O') #define IPATCH_DLS_FOURCC_FMT IPATCH_FOURCC ('f','m','t',' ') #define IPATCH_DLS_FOURCC_DATA IPATCH_FOURCC ('d','a','t','a') /* file chunk sizes */ #define IPATCH_DLS_VERS_SIZE 8 /* version chunk size */ #define IPATCH_DLS_INSH_SIZE 12 /* instrument header chunk size */ #define IPATCH_DLS_RGNH_SIZE 12 /* region header size */ #define IPATCH_DLS_RGNH_LAYER_SIZE 14 /* with optional Layer field */ #define IPATCH_DLS_WLNK_SIZE 12 /* wave link chunk size */ #define IPATCH_DLS_WSMP_HEADER_SIZE 20 /* sample info chunk without loops */ #define IPATCH_DLS_WSMP_LOOP_SIZE 16 /* sample loop size */ #define IPATCH_DLS_ART_HEADER_SIZE 8 /* articulator header size */ #define IPATCH_DLS_CONN_SIZE 12 /* connection block size */ #define IPATCH_DLS_PTBL_HEADER_SIZE 8 /* default pool table header size */ #define IPATCH_DLS_POOLCUE_SIZE 4 /* size of a pool cue offset */ #define IPATCH_DLS_WAVE_FMT_SIZE 16 /* PCM wave fmt chunk size */ #define IPATCH_DLS_INSH_BANK_MASK 0x3FFF #define IPATCH_DLS_INSH_BANK_PERCUSSION (1u << 31u) #define IPATCH_DLS_RGNH_OPTION_SELF_NON_EXCLUSIVE 0x0001 #define IPATCH_DLS_WLNK_PHASE_MASTER 0x0001 #define IPATCH_DLS_WLNK_MULTI_CHANNEL 0x0002 #define IPATCH_DLS_WSMP_NO_TRUNCATION 0x0001 #define IPATCH_DLS_WSMP_NO_COMPRESSION 0x0002 #define IPATCH_DLS_WSMP_LOOP_FORWARD 0x0000 #define IPATCH_DLS_WSMP_LOOP_RELEASE 0x0001 #endif libinstpatch-1.1.6/libinstpatch/IpatchDLSReader.c000066400000000000000000002422051400263525300217360ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchDLSReader * @short_description: DLS version 2 file reader * @see_also: #IpatchDLS * @stability: Stable * * Parses a DLS file into an object tree (#IpatchDLS). */ #include #include #include "IpatchDLSReader.h" #include "IpatchDLSFile.h" #include "IpatchDLSFile_priv.h" #include "IpatchDLS2Region.h" #include "IpatchDLS2Sample.h" #include "IpatchGig.h" #include "IpatchGigEffects.h" #include "IpatchGigFile.h" #include "IpatchGigFile_priv.h" #include "IpatchGigInst.h" #include "IpatchGigRegion.h" #include "IpatchGigSample.h" #include "IpatchSampleStoreFile.h" #include "IpatchSample.h" #include "ipatch_priv.h" #include "i18n.h" #define DEBUG_DLS g_warning #define DEBUG_DLS_UNKNOWN_CHUNK(parser, level) /* #define DEBUG_DLS_UNKNOWN_CHUNK(parser, level) \ g_warning (ipatch_riff_message_detail (parser, -1, "Unknown chunk")) */ /* a slightly sane cap on the max size of an INFO string (1MB comment anyone?) */ #define IPATCH_DLS_MAX_INFO_SIZE (1024 * 1024) /* a size to use for buffered reads or variable length structures - allocated on stack */ #define VARCHUNK_BUF_SIZE 1024 #define DLS_ERROR_MSG "DLS Reader error: %s" #define SET_SIZE_ERROR(parser, level, err) \ g_set_error (err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_SIZE_MISMATCH,\ _(DLS_ERROR_MSG), \ ipatch_riff_message_detail(parser, -1, "Unexpected chunk size")) #define SET_DATA_ERROR(parser, level, err) \ g_set_error (err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_INVALID_DATA,\ _(DLS_ERROR_MSG), \ ipatch_riff_message_detail (parser, -1, "Invalid data")) static void ipatch_dls_reader_class_init(IpatchDLSReaderClass *klass); static void ipatch_dls_reader_init(IpatchDLSReader *reader); static void ipatch_dls_reader_finalize(GObject *object); static gboolean ipatch_dls_reader_start(IpatchDLSReader *reader, GError **err); static gboolean ipatch_dls_reader_fixup(IpatchDLSReader *reader, GError **err); static void ipatch_dls_nullify_fixups(IpatchDLSReader *reader); static gboolean assert_loading_gig(IpatchDLSReader *reader, GError **err); static gboolean ipatch_dls_reader_load_level_0(IpatchDLSReader *reader, GError **err); static gboolean ipatch_dls_reader_load_inst_list(IpatchDLSReader *reader, GError **err); static gboolean ipatch_dls_reader_load_region_list(IpatchDLSReader *reader, IpatchDLS2Inst *inst, GError **err); static gboolean ipatch_gig_reader_load_region_list(IpatchDLSReader *reader, IpatchGigInst *giginst, GError **err); static gboolean ipatch_dls_reader_load_art_list(IpatchDLSReader *reader, GSList **conn_list, GError **err); static gboolean ipatch_dls_reader_load_wave_pool(IpatchDLSReader *reader, GError **err); static gboolean ipatch_gig_reader_load_sub_regions(IpatchDLSReader *reader, IpatchGigRegion *region, GError **err); static gboolean ipatch_gig_reader_load_inst_lart(IpatchDLSReader *reader, IpatchGigInst *inst, GError **err); static gboolean ipatch_dls_load_info(IpatchRiff *riff, IpatchDLS2Info **info, GError **err); static gboolean ipatch_dls_load_region_header(IpatchRiff *riff, IpatchDLS2Region *region, GError **err); static gboolean ipatch_gig_load_region_header(IpatchRiff *riff, IpatchGigRegion *region, GError **err); static gboolean ipatch_dls_load_wave_link(IpatchRiff *riff, IpatchDLS2Region *region, GError **err); static gboolean ipatch_dls_load_sample_info(IpatchRiff *riff, IpatchDLS2SampleInfo *info, GError **err); static gboolean ipatch_dls_load_connection(IpatchRiff *riff, GSList **conn_list, GError **err); static gboolean ipatch_dls_load_sample_format(IpatchRiff *riff, IpatchDLS2Sample *sample, int *bitwidth, int *channels, GError **err); static guint32 *ipatch_dls_load_pool_table(IpatchRiff *riff, guint *size, GError **err); static gboolean ipatch_dls_load_dlid(IpatchRiff *riff, guint8 *dlid, GError **err); static gboolean ipatch_gig_load_sample_info(IpatchRiff *riff, IpatchDLS2SampleInfo *info, GError **err); static gboolean ipatch_gig_load_dimension_info(IpatchRiff *riff, IpatchGigRegion *region, GError **err); static gboolean ipatch_gig_load_dimension_names(IpatchRiff *riff, IpatchGigRegion *region, GError **err); static gboolean ipatch_gig_load_group_names(IpatchRiff *riff, GSList **name_list, GError **err); /* global variables */ static gpointer parent_class = NULL; /** * ipatch_dls_reader_error_quark: (skip) */ GQuark ipatch_dls_reader_error_quark(void) { static GQuark q = 0; if(q == 0) { q = g_quark_from_static_string("DLSReader-error-quark"); } return (q); } GType ipatch_dls_reader_get_type(void) { static GType obj_type = 0; if(!obj_type) { static const GTypeInfo obj_info = { sizeof(IpatchDLSReaderClass), NULL, NULL, (GClassInitFunc)ipatch_dls_reader_class_init, NULL, NULL, sizeof(IpatchDLSReader), 0, (GInstanceInitFunc)ipatch_dls_reader_init, }; obj_type = g_type_register_static(IPATCH_TYPE_RIFF, "IpatchDLSReader", &obj_info, 0); } return (obj_type); } static void ipatch_dls_reader_class_init(IpatchDLSReaderClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->finalize = ipatch_dls_reader_finalize; } static void ipatch_dls_reader_init(IpatchDLSReader *reader) { reader->dls = NULL; reader->needs_fixup = TRUE; reader->is_gig = FALSE; reader->wave_hash = g_hash_table_new(NULL, NULL); } static void ipatch_dls_reader_finalize(GObject *object) { IpatchDLSReader *reader = IPATCH_DLS_READER(object); /* fixup before destroying so bad things don't happen */ if(reader->needs_fixup) { ipatch_dls_nullify_fixups(reader); } /* unref the dls object */ if(reader->dls) { g_object_unref(reader->dls); reader->dls = NULL; } g_hash_table_destroy(reader->wave_hash); reader->wave_hash = NULL; g_free(reader->pool_table); reader->pool_table = NULL; G_OBJECT_CLASS(parent_class)->finalize(object); } /** * ipatch_dls_reader_new: * @handle: DLS file handle to parse or %NULL to set later * * Create a new DLS file reader * * Returns: The new DLS reader */ IpatchDLSReader * ipatch_dls_reader_new(IpatchFileHandle *handle) { IpatchDLSReader *reader; g_return_val_if_fail(!handle || IPATCH_IS_DLS_FILE(handle->file), NULL); reader = g_object_new(IPATCH_TYPE_DLS_READER, NULL); if(handle) { ipatch_riff_set_file_handle(IPATCH_RIFF(reader), handle); } return (reader); } /** * ipatch_dls_reader_load: * @reader: DLS reader object * @err: Location to store error info or %NULL * * Load a DLS file. * * Returns: (transfer full): New DLS object with refcount of 1. */ IpatchDLS2 * ipatch_dls_reader_load(IpatchDLSReader *reader, GError **err) { IpatchRiff *riff; GError *local_err = NULL; g_return_val_if_fail(IPATCH_IS_DLS_READER(reader), NULL); riff = IPATCH_RIFF(reader); g_return_val_if_fail(IPATCH_IS_FILE_HANDLE(riff->handle), NULL); g_return_val_if_fail(!err || !*err, NULL); restart: /* to restart parsing if a DLS file turns out to be GigaSampler */ if(!ipatch_dls_reader_start(reader, err)) { return (NULL); } if(!ipatch_dls_reader_load_level_0(reader, &local_err)) { /* what was thought to be a DLS file turned out to be GigaSampler? */ if(local_err && local_err->domain == IPATCH_DLS_READER_ERROR && local_err->code == IPATCH_DLS_READER_ERROR_GIG) { g_clear_error(&local_err); /* seek back to beginning of file */ if(!ipatch_file_seek(riff->handle, 0, G_SEEK_SET, err)) { return (FALSE); } reader->is_gig = TRUE; g_object_unref(reader->dls); reader->dls = NULL; goto restart; /* restart in GigaSampler mode */ } if(local_err) { g_propagate_error(err, local_err); } return (NULL); } if(!ipatch_dls_reader_fixup(reader, err)) { return (NULL); } /* ++ ref for caller, finalize() will remove reader's ref */ return (g_object_ref(reader->dls)); } /** * ipatch_dls_reader_start: * @reader: DLS/Gig reader * @err: Location to store error info or %NULL * * Starts parsing a DLS/Gig file. This function only needs to be * called if using an IpatchDLSReader without ipatch_dls_load() * (custom readers). The file object of the @reader must be set * before calling this function. Loads the first "DLS" RIFF chunk to * verify we are loading a DLS file and sets other internal variables. * * Returns: %TRUE on success, %FALSE otherwise */ static gboolean ipatch_dls_reader_start(IpatchDLSReader *reader, GError **err) { IpatchRiff *riff; IpatchRiffChunk *chunk; g_return_val_if_fail(IPATCH_IS_DLS_READER(reader), FALSE); riff = IPATCH_RIFF(reader); g_return_val_if_fail(IPATCH_IS_FILE_HANDLE(riff->handle), FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* are we parsing a GigaSampler file? */ if(IPATCH_IS_GIG_FILE(riff->handle->file)) { reader->is_gig = TRUE; } /* start parsing */ if(!(chunk = ipatch_riff_start_read(riff, err))) { return (FALSE); } if(chunk->id != IPATCH_DLS_FOURCC_DLS) { g_set_error(err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_UNEXPECTED_ID, _("Not a DLS file (RIFF id = '%4s')"), chunk->idstr); return (FALSE); } /* ++ ref new object */ if(reader->is_gig) { reader->dls = IPATCH_DLS2(ipatch_gig_new()); } else { reader->dls = ipatch_dls2_new(); } ipatch_dls2_set_file(reader->dls, IPATCH_DLS_FILE(riff->handle->file)); return (TRUE); } /** * ipatch_dls_reader_fixup: * @reader: DLS/Gig reader * @err: Location to store error info or %NULL * * Fixup sample pointers in DLS/GigaSampler regions of the DLS object in * @reader. The sample pointers should be sample pool indexes previously * stored by ipatch_dls_load_wave_link() or ipatch_gig_load_dimension_info(). * The pool table must also have been previously loaded for this to make * any sense. * * Returns: %TRUE on success, %FALSE otherwise */ static gboolean ipatch_dls_reader_fixup(IpatchDLSReader *reader, GError **err) { GHashTable *fixup_hash; IpatchDLS2Sample *sample; IpatchDLS2Inst *inst; IpatchDLS2Region *region; IpatchGigRegion *gig_region; IpatchGigSubRegion *sub_region; IpatchIter inst_iter, region_iter; guint i; g_return_val_if_fail(IPATCH_IS_DLS_READER(reader), FALSE); g_return_val_if_fail(!err || !*err, FALSE); if(!reader->needs_fixup) { return (TRUE); /* already fixed up? */ } fixup_hash = g_hash_table_new(NULL, NULL); /* create pool table index -> sample hash */ i = 0; while(i < reader->pool_table_size) { sample = g_hash_table_lookup(reader->wave_hash, GINT_TO_POINTER(reader->pool_table[i])); if(sample) { g_hash_table_insert(fixup_hash, GINT_TO_POINTER(i), sample); } else { g_warning("Invalid wave pool entry (index=%d)", i); } i++; } if(!reader->is_gig) /* regular DLS file? (not GigaSampler) */ { /* fixup DLS region sample indexes */ if(!ipatch_container_init_iter((IpatchContainer *)(reader->dls), &inst_iter, IPATCH_TYPE_DLS2_INST)) { return (FALSE); } inst = ipatch_dls2_inst_first(&inst_iter); while(inst) /* loop over instruments */ { if(!ipatch_container_init_iter((IpatchContainer *)inst, ®ion_iter, IPATCH_TYPE_DLS2_REGION)) { return (FALSE); } region = ipatch_dls2_region_first(®ion_iter); while(region) /* loop over instrument regions */ { /* region->sample is pool index (ipatch_dls_load_wave_link) */ sample = g_hash_table_lookup(fixup_hash, region->sample); if(!sample) /* fixup failed? */ { char *name; g_object_get(inst, "name", &name, NULL); g_warning("Failed to fixup sample for inst '%s' (index=%d)", name ? name : "", GPOINTER_TO_UINT(region->sample)); g_free(name); region->sample = NULL; ipatch_container_remove(IPATCH_CONTAINER(inst), IPATCH_ITEM(region)); } else { region->sample = NULL; ipatch_dls2_region_set_sample(region, sample); } region = ipatch_dls2_region_next(®ion_iter); } /* while (region) region loop */ inst = ipatch_dls2_inst_next(&inst_iter); } /* while (inst) instrument loop */ } else /* reader->is_gig - fixup GigaSampler sub region sample indexes */ { if(!ipatch_container_init_iter((IpatchContainer *)(reader->dls), &inst_iter, IPATCH_TYPE_GIG_INST)) { return (FALSE); } inst = ipatch_dls2_inst_first(&inst_iter); while(inst) /* loop over instruments */ { if(!ipatch_container_init_iter((IpatchContainer *)inst, ®ion_iter, IPATCH_TYPE_GIG_REGION)) { return (FALSE); } gig_region = ipatch_gig_region_first(®ion_iter); while(gig_region) /* loop over instrument regions */ { for(i = 0; i < gig_region->sub_region_count; i++) { sub_region = gig_region->sub_regions[i]; /* fixup sample index (see ipatch_gig_load_dimension_info) */ sample = g_hash_table_lookup(fixup_hash, sub_region->sample); if(!sample) /* fixup failed? */ { char *name; g_object_get(inst, "name", &name, NULL); g_warning("Failed to fixup sample for inst" " '%s' (index=%d)", name ? name : "", GPOINTER_TO_UINT(sub_region->sample)); g_free(name); sub_region->sample = NULL; } else { sub_region->sample = NULL; ipatch_gig_sub_region_set_sample(sub_region, IPATCH_GIG_SAMPLE(sample)); } } /* for each sub region */ gig_region = ipatch_gig_region_next(®ion_iter); } /* while (gig_region) region loop */ inst = ipatch_dls2_inst_next(&inst_iter); } /* while (inst) instrument loop */ } /* else - GigaSampler file */ g_hash_table_destroy(fixup_hash); /* destroy fixup hash */ reader->needs_fixup = FALSE; /* reset changed state (set by ipatch_dls_reader_fixup()*/ g_object_set (IPATCH_BASE(reader->dls), "changed", FALSE, NULL); return (TRUE); } /* sample index fixups have to be cleared on error, so problems don't occur when DLS object is finalized */ static void ipatch_dls_nullify_fixups(IpatchDLSReader *reader) { IpatchDLS2Inst *inst; IpatchDLS2Region *region; IpatchIter inst_iter, region_iter; int i; if(!ipatch_container_init_iter((IpatchContainer *)(reader->dls), &inst_iter, reader->is_gig ? IPATCH_TYPE_GIG_INST : IPATCH_TYPE_DLS2_INST)) { return; } inst = ipatch_dls2_inst_first(&inst_iter); while(inst) /* loop over instruments */ { if(!ipatch_container_init_iter((IpatchContainer *)inst, ®ion_iter, reader->is_gig ? IPATCH_TYPE_GIG_REGION : IPATCH_TYPE_DLS2_REGION)) { return; } region = ipatch_dls2_region_first(®ion_iter); while(region) /* loop over instrument regions */ { region->sample = NULL; if(reader->is_gig) /* NULLify GigaSampler sample indexes */ { IpatchGigRegion *gig_region = IPATCH_GIG_REGION(region); for(i = 0; i < gig_region->sub_region_count; i++) { gig_region->sub_regions[i]->sample = NULL; } } region = ipatch_dls2_region_next(®ion_iter); } inst = ipatch_dls2_inst_next(&inst_iter); } } /* called before GigaSampler related stuff to ensure that we are already in GigaSampler mode, if not then an error is returned which signals the ipatch_dls_load() routine to restart in GigaSampler mode */ static gboolean assert_loading_gig(IpatchDLSReader *reader, GError **err) { if(reader->is_gig) { return (TRUE); } g_set_error(err, IPATCH_DLS_READER_ERROR, IPATCH_DLS_READER_ERROR_GIG, "GigaSampler file detected, restart loading in gig mode"); return (FALSE); } /** * ipatch_dls_reader_load_level_0: * @reader: DLS/Gig reader * @err: Location to store error info or %NULL * * Load the top level DLS chunk of a DLS or GigaSampler file (essentially * the entire file except the toplevel chunk itself). * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_dls_reader_load_level_0(IpatchDLSReader *reader, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchRiffChunk *chunk; GError *local_err = NULL; g_return_val_if_fail(IPATCH_IS_DLS_READER(reader), FALSE); g_return_val_if_fail(!err || !*err, FALSE); while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_LIST) { switch(chunk->id) { case IPATCH_DLS_FOURCC_LINS: /* instrument list */ if(!ipatch_dls_reader_load_inst_list(reader, err)) { return (FALSE); } break; case IPATCH_DLS_FOURCC_WVPL: /* wave pool list (sample data) */ case IPATCH_DLS_FOURCC_DWPL: /* WTF? - Seen in some DLS1 files */ if(!ipatch_dls_reader_load_wave_pool(reader, err)) { return (FALSE); } break; case IPATCH_DLS_FOURCC_INFO: /* toplevel INFO */ if(!ipatch_dls_load_info(riff, &reader->dls->info, err)) { return (FALSE); } break; case IPATCH_GIG_FOURCC_3GRI: if(!assert_loading_gig(reader, err)) { return (FALSE); } if(!ipatch_gig_load_group_names(riff, &(IPATCH_GIG(reader->dls)->group_names), err)) { return (FALSE); } break; default: DEBUG_DLS_UNKNOWN_CHUNK(riff, -1); break; } } else /* a sub chunk */ { switch(chunk->id) { case IPATCH_DLS_FOURCC_CDL: /* toplevel conditional chunk */ DEBUG_DLS("Toplevel DLS CDL chunk!\n"); break; case IPATCH_DLS_FOURCC_VERS: /* file version chunk */ if(chunk->size != IPATCH_DLS_VERS_SIZE) { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } if(!ipatch_file_buf_load(riff->handle, IPATCH_DLS_VERS_SIZE, err)) { return (FALSE); } reader->dls->ms_version = ipatch_file_buf_read_u32(riff->handle); reader->dls->ls_version = ipatch_file_buf_read_u32(riff->handle); ipatch_item_set_flags(IPATCH_ITEM(reader->dls), IPATCH_DLS2_VERSION_SET); break; case IPATCH_DLS_FOURCC_DLID: /* globally unique identifier */ if(!reader->dls->dlid) { reader->dls->dlid = g_malloc(16); } if(!ipatch_dls_load_dlid(riff, reader->dls->dlid, err)) { return (FALSE); } break; case IPATCH_DLS_FOURCC_COLH: /* collection header (inst count) */ /* we don't care since instruments are dynamically loaded */ break; case IPATCH_DLS_FOURCC_PTBL: /* pool table (sample mappings) */ reader->pool_table = ipatch_dls_load_pool_table (riff, &reader->pool_table_size, &local_err); if(local_err) { g_propagate_error(err, local_err); return (FALSE); } break; case IPATCH_GIG_FOURCC_EINF: /* FIXME - unknown */ break; default: DEBUG_DLS_UNKNOWN_CHUNK(riff, -1); break; } } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } return (ipatch_riff_get_error(riff, NULL)); } /** * ipatch_dls_reader_load_inst_list: * @reader: DLS/Gig reader * @err: Location to store error info or %NULL * * Loads DLS or GigaSampler instrument list from the current position in * the file assigned to @reader. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_dls_reader_load_inst_list(IpatchDLSReader *reader, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchRiffChunk *chunk; IpatchIter iter; guint32 uint; gboolean retval; g_return_val_if_fail(IPATCH_IS_DLS_READER(reader), FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* initialize iterator to instrument list */ if(!ipatch_container_init_iter(IPATCH_CONTAINER(reader->dls), &iter, reader->is_gig ? IPATCH_TYPE_GIG_INST : IPATCH_TYPE_DLS2_INST)) { return (FALSE); } while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_LIST && /* an INS list? */ chunk->id == IPATCH_DLS_FOURCC_INS) { IpatchDLS2Inst *inst; /* ++ ref new instrument and append it in DLS2/Gig object */ if(reader->is_gig) { inst = IPATCH_DLS2_INST(ipatch_gig_inst_new()); } else { inst = ipatch_dls2_inst_new(); } ipatch_container_insert_iter((IpatchContainer *)(reader->dls), (IpatchItem *)inst, &iter); g_object_unref(inst); /* -- unref new instrument (parented) */ while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_LIST) { switch(chunk->id) /* list chunk? */ { case IPATCH_DLS_FOURCC_LRGN: /* region list */ if(!reader->is_gig) retval = ipatch_dls_reader_load_region_list(reader, inst, err); else retval = ipatch_gig_reader_load_region_list(reader, IPATCH_GIG_INST(inst), err); if(!retval) { return (FALSE); } break; case IPATCH_DLS_FOURCC_LART: /* DLS1 articulator list */ if(reader->is_gig) { /* load GigaSampler 3ewg chunk */ if(!ipatch_gig_reader_load_inst_lart(reader, IPATCH_GIG_INST(inst), err)) { return (FALSE); } break; } /* fall through */ case IPATCH_DLS_FOURCC_LAR2: /* DLS2 articulator list */ if(!ipatch_dls_reader_load_art_list(reader, &inst->conns, err)) { return (FALSE); } break; case IPATCH_DLS_FOURCC_INFO: /* instrument INFO */ if(!ipatch_dls_load_info(riff, &inst->info, err)) { return (FALSE); } break; default: DEBUG_DLS_UNKNOWN_CHUNK(riff, -1); break; } } else /* sub chunk */ { switch(chunk->id) { case IPATCH_DLS_FOURCC_INSH: /* instrument header */ if(chunk->size != IPATCH_DLS_INSH_SIZE) { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } if(!ipatch_file_buf_load(riff->handle, chunk->size, err)) { return (FALSE); } /* we ignore the region count */ ipatch_file_buf_skip(riff->handle, 4); uint = ipatch_file_buf_read_u32(riff->handle); inst->bank = uint & IPATCH_DLS_INSH_BANK_MASK; if(uint & IPATCH_DLS_INSH_BANK_PERCUSSION) { ipatch_item_set_flags(inst, IPATCH_DLS2_INST_PERCUSSION); } inst->program = ipatch_file_buf_read_u32(riff->handle); break; case IPATCH_DLS_FOURCC_DLID: /* globally unique ID */ if(!inst->dlid) { inst->dlid = g_malloc(16); } if(!ipatch_dls_load_dlid(riff, inst->dlid, err)) { return (FALSE); } break; default: DEBUG_DLS_UNKNOWN_CHUNK(riff, -1); break; } } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } inst->conns = g_slist_reverse(inst->conns); if(!ipatch_riff_get_error(riff, NULL)) { return (FALSE); } } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } return (ipatch_riff_get_error(riff, NULL)); } /** * ipatch_dls_reader_load_region_list: * @reader: DLS reader * @inst: DLS instrument to fill with loaded regions * @err: Location to store error info or %NULL * * Loads DLS region list into an @inst object from the current * position in the file assigned to @reader. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_dls_reader_load_region_list(IpatchDLSReader *reader, IpatchDLS2Inst *inst, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchIter iter; IpatchRiffChunk *chunk; IpatchDLS2Region *region; g_return_val_if_fail(IPATCH_IS_DLS_READER(reader), FALSE); g_return_val_if_fail(IPATCH_IS_DLS2_INST(inst), FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* initialize iterator to DLS2 region list */ if(!ipatch_container_init_iter(IPATCH_CONTAINER(inst), &iter, IPATCH_TYPE_DLS2_REGION)) { return (FALSE); } while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_LIST && (chunk->id == IPATCH_DLS_FOURCC_RGN || chunk->id == IPATCH_DLS_FOURCC_RGN2)) { region = ipatch_dls2_region_new(); ipatch_container_insert_iter((IpatchContainer *)inst, (IpatchItem *)region, &iter); g_object_unref(region); /* -- unref new region (parented) */ while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_LIST) /* list chunk? */ { switch(chunk->id) { case IPATCH_DLS_FOURCC_LART: case IPATCH_DLS_FOURCC_LAR2: if(!ipatch_dls_reader_load_art_list(reader, ®ion->conns, err)) { return (FALSE); } break; case IPATCH_DLS_FOURCC_INFO: if(!ipatch_dls_load_info(riff, ®ion->info, err)) { return (FALSE); } break; case IPATCH_GIG_FOURCC_3PRG: /* gig sub region list */ assert_loading_gig(reader, err); return (FALSE); case IPATCH_GIG_FOURCC_3DNL: /* dimension names */ assert_loading_gig(reader, err); return (FALSE); default: DEBUG_DLS_UNKNOWN_CHUNK(riff, -1); break; } } else /* sub chunk */ { switch(chunk->id) { case IPATCH_DLS_FOURCC_RGNH: if(!ipatch_dls_load_region_header(riff, region, err)) { return (FALSE); } break; case IPATCH_DLS_FOURCC_WLNK: if(!ipatch_dls_load_wave_link(riff, region, err)) { return (FALSE); } break; case IPATCH_DLS_FOURCC_WSMP: if(!region->sample_info) /* for sanity */ { region->sample_info = ipatch_dls2_sample_info_new(); } if(!ipatch_dls_load_sample_info(riff, region->sample_info, err)) { return (FALSE); } break; case IPATCH_DLS_FOURCC_CDL: DEBUG_DLS("Region CDL chunk!\n"); break; case IPATCH_GIG_FOURCC_3LNK: /* Gig dimension info */ assert_loading_gig(reader, err); return (FALSE); break; case IPATCH_GIG_FOURCC_3DDP: /* FIXME - what is it? */ assert_loading_gig(reader, err); return (FALSE); default: DEBUG_DLS_UNKNOWN_CHUNK(riff, -1); break; } } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } if(!ipatch_riff_get_error(riff, NULL)) { return (FALSE); } } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } return (ipatch_riff_get_error(riff, NULL)); } /** * ipatch_gig_reader_load_region_list: * @reader: DLS/Gig reader * @giginst: Gig instrument to fill with loaded regions * @err: Location to store error info or %NULL * * Loads GigaSampler region list into an @inst object from the current * position in the file assigned to @reader. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_gig_reader_load_region_list(IpatchDLSReader *reader, IpatchGigInst *giginst, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchIter iter; IpatchRiffChunk *chunk; IpatchDLS2Inst *inst; IpatchGigRegion *region; g_return_val_if_fail(IPATCH_IS_DLS_READER(reader), FALSE); g_return_val_if_fail(IPATCH_IS_GIG_INST(giginst), FALSE); g_return_val_if_fail(!err || !*err, FALSE); inst = IPATCH_DLS2_INST(giginst); /* initialize iterator to Gig region list */ if(!ipatch_container_init_iter(IPATCH_CONTAINER(inst), &iter, IPATCH_TYPE_GIG_REGION)) { return (FALSE); } while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_LIST && (chunk->id == IPATCH_DLS_FOURCC_RGN || chunk->id == IPATCH_DLS_FOURCC_RGN2)) { region = ipatch_gig_region_new(); ipatch_container_insert_iter((IpatchContainer *)inst, (IpatchItem *)region, &iter); g_object_unref(region); /* -- unref new region (parented) */ while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_LIST) /* list chunk? */ { switch(chunk->id) { case IPATCH_DLS_FOURCC_INFO: if(!ipatch_dls_load_info(riff, ®ion->info, err)) { return (FALSE); } break; case IPATCH_GIG_FOURCC_3PRG: /* gig sub region list */ if(!ipatch_gig_reader_load_sub_regions(reader, region, err)) { return (FALSE); } break; case IPATCH_GIG_FOURCC_3DNL: /* dimension names */ if(!ipatch_gig_load_dimension_names(riff, region, err)) { return (FALSE); } break; default: DEBUG_DLS_UNKNOWN_CHUNK(riff, -1); break; } } else /* sub chunk */ { switch(chunk->id) { case IPATCH_DLS_FOURCC_RGNH: if(!ipatch_gig_load_region_header(riff, region, err)) { return (FALSE); } break; case IPATCH_DLS_FOURCC_WLNK: /* ignore WLNK chunks with GigaSampler files */ break; case IPATCH_DLS_FOURCC_WSMP: /* ignore useless sample info for GigaSampler files */ break; case IPATCH_DLS_FOURCC_CDL: DEBUG_DLS("Region CDL chunk!\n"); break; case IPATCH_GIG_FOURCC_3LNK: /* dimension info */ if(!ipatch_gig_load_dimension_info(riff, region, err)) { return (FALSE); } break; case IPATCH_GIG_FOURCC_3DDP: /* FIXME - what is it? */ if(chunk->size == IPATCH_GIG_3DDP_SIZE) if(!ipatch_file_read(riff->handle, &(IPATCH_GIG_REGION(region)->chunk_3ddp), IPATCH_GIG_3DDP_SIZE, err)) { return (FALSE); } break; default: DEBUG_DLS_UNKNOWN_CHUNK(riff, -1); break; } } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } if(!ipatch_riff_get_error(riff, NULL)) { return (FALSE); } } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } return (ipatch_riff_get_error(riff, NULL)); } /** * ipatch_dls_reader_load_art_list: * @reader: DLS/Gig reader * @conn_list: (out) (element-type IpatchDLS2Conn): Pointer to a list to populate * @err: Location to store error info or %NULL * * Loads DLS or GigaSampler articulator list from the current position in * the file assigned to @reader. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_dls_reader_load_art_list(IpatchDLSReader *reader, GSList **conn_list, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchRiffChunk *chunk; g_return_val_if_fail(IPATCH_IS_DLS_READER(reader), FALSE); g_return_val_if_fail(conn_list != NULL, FALSE); g_return_val_if_fail(!err || !*err, FALSE); while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_SUB) { switch(chunk->id) { case IPATCH_DLS_FOURCC_ART1: case IPATCH_DLS_FOURCC_ART2: if(!ipatch_dls_load_connection(riff, conn_list, err)) { return (FALSE); } break; case IPATCH_DLS_FOURCC_CDL: DEBUG_DLS("Articulator CDL chunk!\n"); break; default: DEBUG_DLS_UNKNOWN_CHUNK(riff, -1); break; } } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } return (ipatch_riff_get_error(riff, NULL)); } /** * ipatch_dls_reader_load_wave_pool: * @reader: DLS/Gig reader * @err: Location to store error info or %NULL * * Loads DLS or GigaSampler wave pool ("wvpl" chunk) from the current position * in the file assigned to @reader. Populates the @reader wave pool hash with * sample offsets for later fixup. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_dls_reader_load_wave_pool(IpatchDLSReader *reader, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchRiffChunk *chunk; IpatchDLS2Sample *sample; IpatchSampleData *sampledata; IpatchSample *store; IpatchIter iter; guint data_ofs, wave_ofs, data_size; int bitwidth; int channels; int format; g_return_val_if_fail(IPATCH_IS_DLS_READER(reader), FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* initialize iterator to sample list */ if(!ipatch_container_init_iter(IPATCH_CONTAINER(reader->dls), &iter, IPATCH_TYPE_DLS2_SAMPLE)) { return (FALSE); } while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_LIST && chunk->id == IPATCH_DLS_FOURCC_WAVE) { /* offset to wave LIST header in wave pool chunk */ wave_ofs = chunk[-1].position - (IPATCH_RIFF_LIST_HEADER_SIZE + IPATCH_RIFF_FOURCC_SIZE); if(!reader->is_gig) { sample = ipatch_dls2_sample_new(); /* ++ ref and add new sample */ } else /* ++ ref new gig sample */ { sample = IPATCH_DLS2_SAMPLE(ipatch_gig_sample_new()); } ipatch_container_insert_iter((IpatchContainer *)(reader->dls), (IpatchItem *)sample, &iter); g_object_unref(sample); /* -- unref new sample */ data_size = 0; data_ofs = 0; bitwidth = 0; channels = 0; while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_SUB) /* sub chunk? */ { switch(chunk->id) { case IPATCH_DLS_FOURCC_FMT: if(!ipatch_dls_load_sample_format (riff, sample, &bitwidth, &channels, err)) { return (FALSE); } break; case IPATCH_DLS_FOURCC_DATA: /* position in file to sample data */ data_ofs = ipatch_riff_get_position(riff); data_size = chunk->size; break; case IPATCH_DLS_FOURCC_WSMP: if(!sample->sample_info) /* for sanity */ { sample->sample_info = ipatch_dls2_sample_info_new(); } if(!ipatch_dls_load_sample_info(riff, sample->sample_info, err)) { return (FALSE); } break; case IPATCH_GIG_FOURCC_SMPL: /* GigaSampler sample info */ /* Have seen in non gig files, just ignore it then */ if(!reader->is_gig) { break; } if(!sample->sample_info) /* for sanity */ { sample->sample_info = ipatch_dls2_sample_info_new(); } if(!ipatch_gig_load_sample_info(riff, sample->sample_info, err)) { return (FALSE); } break; case IPATCH_GIG_FOURCC_3GIX: if(!assert_loading_gig(reader, err)) { return (FALSE); } if(chunk->size == IPATCH_GIG_3GIX_SIZE) { /* Sample group # - FIXME - Is it really 32 bits? */ if(!ipatch_file_read_u32(riff->handle, &(IPATCH_GIG_SAMPLE(sample)->group_number), err)) { return (FALSE); } } break; case IPATCH_DLS_FOURCC_DLID: if(!sample->dlid) { sample->dlid = g_malloc(16); } if(!ipatch_dls_load_dlid(riff, sample->dlid, err)) { return (FALSE); } break; default: DEBUG_DLS_UNKNOWN_CHUNK(riff, -1); break; } } else if(chunk->id == IPATCH_DLS_FOURCC_INFO) /* info list? */ { if(!ipatch_dls_load_info(riff, &sample->info, err)) { return (FALSE); } } else { DEBUG_DLS_UNKNOWN_CHUNK(riff, -1); } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } /* while() - wave LIST chunk */ if(!ipatch_riff_get_error(riff, NULL)) { return (FALSE); } /* format and data chunks processed? */ if(bitwidth && channels && data_size && data_ofs) { /* convert data size to samples */ data_size = data_size / (bitwidth / 8) / channels; /* add wave LIST chunk offset to wave hash (fixup later) */ g_hash_table_insert(reader->wave_hash, GUINT_TO_POINTER(wave_ofs), sample); format = (bitwidth == 8 ? IPATCH_SAMPLE_8BIT : IPATCH_SAMPLE_16BIT) | (channels == 2 ? IPATCH_SAMPLE_STEREO : IPATCH_SAMPLE_MONO) | (bitwidth == 8 ? IPATCH_SAMPLE_UNSIGNED : IPATCH_SAMPLE_SIGNED) | IPATCH_SAMPLE_LENDIAN; /* ++ ref new store */ store = ipatch_sample_store_file_new(riff->handle->file, data_ofs); g_object_set(store, "sample-size", data_size, "sample-format", format, "sample-rate", sample->rate, NULL); sampledata = ipatch_sample_data_new(); /* ++ ref sample data */ ipatch_sample_data_add(sampledata, IPATCH_SAMPLE_STORE(store)); ipatch_dls2_sample_set_data(sample, sampledata); g_object_unref(store); /* -- unref store */ g_object_unref(sampledata); /* -- unref sampledata */ } else { /* !!! don't use sample after removed */ g_warning(_("Invalid sample")); ipatch_item_remove((IpatchItem *)sample); } } /* if wave LIST chunk */ if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } /* while() - wave pool */ return (ipatch_riff_get_error(riff, NULL)); } /** * ipatch_gig_reader_load_sub_regions: * @reader: Gig reader * @region: Gig region to load GigaSampler sub regions into * @err: Location to store error info or %NULL * * Loads GigaSampler sub regions ("3prg" chunk) from the current position * in the file assigned to @reader. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_gig_reader_load_sub_regions(IpatchDLSReader *reader, IpatchGigRegion *region, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchRiffChunk *chunk; IpatchGigSubRegion *sub_region; int sub_region_index = 0; g_return_val_if_fail(IPATCH_IS_DLS_READER(reader), FALSE); g_return_val_if_fail(IPATCH_IS_GIG_REGION(region), FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* loop in "3prg" chunk */ while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_LIST && chunk->id == IPATCH_GIG_FOURCC_3EWL) { /* loop in "3ewl" chunk */ if(sub_region_index >= region->sub_region_count) { /* shouldn't happen, but just in case */ g_warning("GigaSampler sub region count mismatch"); if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } break; } sub_region = region->sub_regions[sub_region_index]; while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_SUB) /* sub chunk? */ { switch(chunk->id) { case IPATCH_DLS_FOURCC_WSMP: /* ipatch_gig_sub_region_set_sample_info_override would probably be cleaner, but sub_region->sample might not be valid (index that is fixed up later) */ if(!sub_region->sample_info) { sub_region->sample_info = ipatch_dls2_sample_info_new(); ipatch_item_set_flags(sub_region, IPATCH_GIG_SUB_REGION_SAMPLE_INFO_OVERRIDE); } if(!ipatch_dls_load_sample_info(riff, sub_region->sample_info, err)) { return (FALSE); } break; case IPATCH_GIG_FOURCC_3EWA: /* GigaSampler effects */ if(chunk->size != IPATCH_GIG_3EWA_SIZE) { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } /* load effects chunk into buffer */ if(!ipatch_file_buf_load(riff->handle, IPATCH_GIG_3EWA_SIZE, err)) { return (FALSE); } ipatch_gig_parse_effects(riff->handle, &sub_region->effects); break; default: DEBUG_DLS_UNKNOWN_CHUNK(riff, -1); break; } } else { DEBUG_DLS_UNKNOWN_CHUNK(riff, -1); } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } /* while() - "3ewl" LIST chunk */ if(!ipatch_riff_get_error(riff, NULL)) { return (FALSE); } sub_region_index++; /* advance to next sub region */ } /* if "3ewl" LIST chunk */ if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } /* while() - "3prg" chunk */ return (ipatch_riff_get_error(riff, NULL)); } /** * ipatch_gig_reader_load_inst_lart: * @reader: Gig reader * @err: Location to store error info or %NULL * * Loads a GigaSampler global parameter chunk '3ewg' from an instrument 'lart' * list. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_gig_reader_load_inst_lart(IpatchDLSReader *reader, IpatchGigInst *inst, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchRiffChunk *chunk; g_return_val_if_fail(IPATCH_IS_DLS_READER(reader), FALSE); g_return_val_if_fail(IPATCH_IS_GIG_INST(inst), FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* no chunks? - return */ if(!(chunk = ipatch_riff_read_chunk(riff, err))) { return (ipatch_riff_get_error(riff, NULL)); } /* not a '3ewg' chunk? - return */ if(chunk->type != IPATCH_RIFF_CHUNK_SUB || chunk->id != IPATCH_GIG_FOURCC_3EWG || chunk->size != IPATCH_GIG_3EWG_SIZE) { if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } return (TRUE); } /* read the 3ewg chunk data */ if(!ipatch_file_read(riff->handle, &inst->chunk_3ewg, IPATCH_GIG_3EWG_SIZE, err)) { return (FALSE); } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } return (TRUE); } /** * ipatch_dls_load_info: * @riff: RIFF parser * @info: Pointer to DLS info list * @err: Location to store error info or %NULL * * Loads DLS or GigaSampler info from the current position in the file * assigned to @riff. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_dls_load_info(IpatchRiff *riff, IpatchDLS2Info **info, GError **err) { IpatchRiffChunk *chunk; guint32 size; char *str; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(info != NULL, FALSE); g_return_val_if_fail(!err || !*err, FALSE); while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_SUB && chunk->size > 0) { size = chunk->size; if(size > IPATCH_DLS_MAX_INFO_SIZE) { size = IPATCH_DLS_MAX_INFO_SIZE; } str = g_malloc(size); if(!ipatch_file_read(riff->handle, str, size, err)) { g_free(str); return (FALSE); } str[size - 1] = '\0'; /* force terminate in case it isn't */ ipatch_dls2_info_set(info, chunk->id, str); g_free(str); } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } return (ipatch_riff_get_error(riff, NULL)); } /** * ipatch_dls_load_region_header: * @riff: RIFF parser * @region: DLS region to store header info in * @err: Location to store error info or %NULL * * Loads DLS instrument region header ("rgnh" chunk) * from the current position in the file assigned to @riff. The "rgnh" chunk * header should have already been loaded. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_dls_load_region_header(IpatchRiff *riff, IpatchDLS2Region *region, GError **err) { IpatchRiffChunk *chunk; guint16 options; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(IPATCH_IS_DLS2_REGION(region), FALSE); g_return_val_if_fail(!err || !*err, FALSE); chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size != IPATCH_DLS_RGNH_SIZE && chunk->size != IPATCH_DLS_RGNH_LAYER_SIZE) { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } if(!ipatch_file_buf_load(riff->handle, chunk->size, err)) { return (FALSE); } region->note_range_low = (guint8)ipatch_file_buf_read_u16(riff->handle); region->note_range_high = (guint8)ipatch_file_buf_read_u16(riff->handle); region->velocity_range_low = (guint8)ipatch_file_buf_read_u16(riff->handle); region->velocity_range_high = (guint8)ipatch_file_buf_read_u16(riff->handle); /* ISOK? Undefined flags are discarded! */ options = ipatch_file_buf_read_u16(riff->handle); if(options & IPATCH_DLS_RGNH_OPTION_SELF_NON_EXCLUSIVE) { ipatch_item_set_flags(region, IPATCH_DLS2_REGION_SELF_NON_EXCLUSIVE); } region->key_group = ipatch_file_buf_read_u16(riff->handle); if(chunk->size == IPATCH_DLS_RGNH_LAYER_SIZE) /* optional layer field? */ { region->layer_group = ipatch_file_buf_read_u16(riff->handle); } return (TRUE); } /** * ipatch_gig_load_region_header: * @riff: RIFF parser * @region: Gig region to store header info in * @err: Location to store error info or %NULL * * Loads GigaSampler instrument region header ("rgnh" chunk) * from the current position in the file assigned to @riff. The "rgnh" chunk * header should have already been loaded. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_gig_load_region_header(IpatchRiff *riff, IpatchGigRegion *region, GError **err) { IpatchRiffChunk *chunk; guint16 options; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(IPATCH_IS_GIG_REGION(region), FALSE); g_return_val_if_fail(!err || !*err, FALSE); chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size != IPATCH_DLS_RGNH_SIZE && chunk->size != IPATCH_DLS_RGNH_LAYER_SIZE) { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } if(!ipatch_file_buf_load(riff->handle, chunk->size, err)) { return (FALSE); } region->note_range_low = (guint8)ipatch_file_buf_read_u16(riff->handle); region->note_range_high = (guint8)ipatch_file_buf_read_u16(riff->handle); region->velocity_range_low = (guint8)ipatch_file_buf_read_u16(riff->handle); region->velocity_range_high = (guint8)ipatch_file_buf_read_u16(riff->handle); /* ISOK? Undefined flags are discarded! */ options = ipatch_file_buf_read_u16(riff->handle); if(options & IPATCH_DLS_RGNH_OPTION_SELF_NON_EXCLUSIVE) { ipatch_item_set_flags(region, IPATCH_GIG_REGION_SELF_NON_EXCLUSIVE); } region->key_group = ipatch_file_buf_read_u16(riff->handle); if(chunk->size == IPATCH_DLS_RGNH_LAYER_SIZE) /* optional layer field? */ { region->layer_group = ipatch_file_buf_read_u16(riff->handle); } return (TRUE); } /** * ipatch_dls_load_wave_link: * @riff: RIFF parser * @region: DLS region to load info into * @err: Location to store error info or %NULL * * Loads DLS wave link chunk ("wlnk" chunk) from the current position in the * file assigned to @riff. The "wlnk" chunk header should have already been * loaded. * * NOTE: Sample pool index is stored in @region sample pointer. This index * should be fixed up or cleared before the region is freed otherwise bad * things will happen. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_dls_load_wave_link(IpatchRiff *riff, IpatchDLS2Region *region, GError **err) { IpatchRiffChunk *chunk; guint16 options; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(IPATCH_IS_DLS2_REGION(region), FALSE); g_return_val_if_fail(!err || !*err, FALSE); chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size != IPATCH_DLS_WLNK_SIZE) { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } if(!ipatch_file_buf_load(riff->handle, chunk->size, err)) { return (FALSE); } /* ISOK? Undefined flags are discarded! */ options = ipatch_file_buf_read_u16(riff->handle); if(options & IPATCH_DLS_WLNK_PHASE_MASTER) { ipatch_item_set_flags(region, IPATCH_DLS2_REGION_PHASE_MASTER); } if(options & IPATCH_DLS_WLNK_MULTI_CHANNEL) { ipatch_item_set_flags(region, IPATCH_DLS2_REGION_MULTI_CHANNEL); } region->phase_group = ipatch_file_buf_read_u16(riff->handle); region->channel = ipatch_file_buf_read_u32(riff->handle); /* store sample pool index in sample pointer (for later fixup) */ region->sample = GINT_TO_POINTER(ipatch_file_buf_read_u32(riff->handle)); return (TRUE); } /** * ipatch_dls_load_sample_info: * @riff: RIFF parser * @info: (out): Sample info structure to load info into * @err: Location to store error info or %NULL * * Loads DLS or GigaSampler sample info ("wsmp" chunk) from the * current position in the file assigned to @riff. The "wsmp" chunk header * should already have been loaded. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_dls_load_sample_info(IpatchRiff *riff, IpatchDLS2SampleInfo *info, GError **err) { IpatchRiffChunk *chunk; guint32 options, struct_size, loop_count, loop_type; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(info != NULL, FALSE); g_return_val_if_fail(!err || !*err, FALSE); chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size > VARCHUNK_BUF_SIZE || chunk->size < IPATCH_DLS_WSMP_HEADER_SIZE) { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } if(!ipatch_file_buf_load(riff->handle, chunk->size, err)) { return (FALSE); } struct_size = ipatch_file_buf_read_u32(riff->handle); if(struct_size < IPATCH_DLS_WSMP_HEADER_SIZE || struct_size & 1) { /* structure size sanity check */ SET_DATA_ERROR(riff, -1, err); return (FALSE); } info->root_note = (guint8)ipatch_file_buf_read_u16(riff->handle); info->fine_tune = ipatch_file_buf_read_u16(riff->handle); info->gain = ipatch_file_buf_read_u32(riff->handle); /* ISOK? Undefined flags are discarded! */ options = ipatch_file_buf_read_u32(riff->handle); if(options & IPATCH_DLS_WSMP_NO_TRUNCATION) { info->options |= IPATCH_DLS2_SAMPLE_NO_TRUNCATION; } if(options & IPATCH_DLS_WSMP_NO_COMPRESSION) { info->options |= IPATCH_DLS2_SAMPLE_NO_COMPRESSION; } /* skip header expansion data (if any) */ ipatch_file_buf_skip(riff->handle, struct_size - IPATCH_DLS_WSMP_HEADER_SIZE); loop_count = ipatch_file_buf_read_u32(riff->handle); /* we only support 1 loop, but work even if > 1 (spec says undefined) */ if(loop_count > 0 && chunk->size >= struct_size + IPATCH_DLS_WSMP_LOOP_SIZE) { ipatch_file_buf_skip(riff->handle, 4); /* skip loop structure size */ loop_type = ipatch_file_buf_read_u32(riff->handle); if(loop_type == IPATCH_DLS_WSMP_LOOP_RELEASE) { info->options |= IPATCH_SAMPLE_LOOP_RELEASE; } else { info->options |= IPATCH_SAMPLE_LOOP_STANDARD; /* default */ } info->loop_start = ipatch_file_buf_read_u32(riff->handle); info->loop_end = info->loop_start + ipatch_file_buf_read_u32(riff->handle); } return (TRUE); } /** * ipatch_dls_load_connection: * @riff: RIFF parser * @conn_list: (out) (element-type IpatchDLS2Conn): Pointer to a list to populate * with loaded #IpatchDLS2Conn structures. * @err: Location to store error info or %NULL * * Load a DLS articulator chunk ("art1" or "art2") containing connection * blocks which are loded into @conn_list. The articulation chunk header * should already have been loaded. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_dls_load_connection(IpatchRiff *riff, GSList **conn_list, GError **err) { IpatchDLS2Conn *conn; IpatchRiffChunk *chunk; int header_size, count, i; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(conn_list != NULL, FALSE); g_return_val_if_fail(!err || !*err, FALSE); chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size < IPATCH_DLS_ART_HEADER_SIZE) { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } if(!ipatch_file_buf_load(riff->handle, IPATCH_DLS_ART_HEADER_SIZE, err)) { return (FALSE); } header_size = ipatch_file_buf_read_u32(riff->handle); if(header_size < IPATCH_DLS_ART_HEADER_SIZE || header_size & 1) { /* make sure art header size is sane */ SET_DATA_ERROR(riff, -1, err); return (FALSE); } /* check for header expansion */ if(header_size > IPATCH_DLS_ART_HEADER_SIZE) { /* skip expansion data */ if(!ipatch_file_seek(riff->handle, header_size - IPATCH_DLS_ART_HEADER_SIZE, G_SEEK_CUR, err)) { return (FALSE); } /* load connection count (last field before connection blocks) */ if(!ipatch_file_buf_load(riff->handle, 4, err)) { return (FALSE); } } count = ipatch_file_buf_read_u32(riff->handle); if(chunk->size != header_size + count * IPATCH_DLS_CONN_SIZE) { /* make sure connection block count is sane */ SET_SIZE_ERROR(riff, -1, err); return (FALSE); } if(!count) { return (TRUE); } if(!ipatch_file_buf_load(riff->handle, chunk->size - header_size, err)) { return (FALSE); } for(i = count; i > 0; i--) /* parse connection blocks */ { conn = ipatch_dls2_conn_new(); *conn_list = g_slist_prepend(*conn_list, conn); conn->src = ipatch_file_buf_read_u16(riff->handle); conn->ctrlsrc = ipatch_file_buf_read_u16(riff->handle); conn->dest = ipatch_file_buf_read_u16(riff->handle); conn->trans = ipatch_file_buf_read_u16(riff->handle); conn->scale = ipatch_file_buf_read_s32(riff->handle); } return (TRUE); } /** * ipatch_dls_load_sample_format: * @riff: RIFF parser * @sample: DLS sample to load data into * @bitwidth: (out) (optional): Pointer to an integer to fill with the * sample's bit width or %NULL * @channels: (out) (optional): Pointer to an integer to fill with the * number of channels or %NULL * @err: Location to store error info or %NULL * * Parses DLS sample format info ("fmt " chunk) from the current * position in the file assigned to the @riff (chunk header should already * be loaded). * * Returns: %TRUE on success, %FALSE on error (in which case @err may be set). */ static gboolean ipatch_dls_load_sample_format(IpatchRiff *riff, IpatchDLS2Sample *sample, int *bitwidth, int *channels, GError **err) { IpatchRiffChunk *chunk; guint16 i16; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(IPATCH_IS_DLS2_SAMPLE(sample), FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* since it seems there are various length FMT chunks, we only assert that it is at least the size of the default PCM format chunk. sizes seen: (18 bytes..) */ chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size < IPATCH_DLS_WAVE_FMT_SIZE) { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } if(!ipatch_file_buf_load(riff->handle, IPATCH_DLS_WAVE_FMT_SIZE, err)) { return (FALSE); } i16 = ipatch_file_buf_read_u16(riff->handle); if(i16 != IPATCH_RIFF_WAVE_FMT_PCM) /* assert PCM wave data */ { SET_DATA_ERROR(riff, -1, err); return (FALSE); } i16 = ipatch_file_buf_read_u16(riff->handle); if(i16 != 1 && i16 != 2) /* assert 1 or 2 channel data */ { SET_DATA_ERROR(riff, -1, err); return (FALSE); } if(channels) { *channels = i16; } sample->rate = ipatch_file_buf_read_u32(riff->handle); /* skip useless dwAvgBytesPerSec and wBlockAlign fields */ ipatch_file_buf_skip(riff->handle, 6); /* load bit width */ i16 = ipatch_file_buf_read_u16(riff->handle); if(i16 != 8 && i16 != 16) /* FIXME - Support higher bit widths */ { SET_DATA_ERROR(riff, -1, err); return (FALSE); } if(bitwidth) { *bitwidth = i16; } return (TRUE); } /** * ipatch_dls_load_pool_table: * @riff: RIFF parser * @size: (out): Pointer to an unsigned integer to store number of entries in * returned pool table array * @err: Location to store error info or %NULL * * Load a sample pool table ("ptbl" chunk) of a DLS or GigaSampler * file from the current position in the file assigned to @riff (chunk * header should already be loaded). * * Returns: (array length=size): A newly allocated array of 32bit integers * for each entry in the pool table, or %NULL if empty pool table or on * error (in which case @err may be set). Free the table when finished with it. */ static guint32 * ipatch_dls_load_pool_table(IpatchRiff *riff, guint *size, GError **err) { IpatchRiffChunk *chunk; guint32 *cuep, *tmpcuep; guint header_size, count; if(size) { *size = 0; } g_return_val_if_fail(IPATCH_IS_RIFF(riff), NULL); g_return_val_if_fail(size != NULL, NULL); g_return_val_if_fail(!err || !*err, NULL); chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size < IPATCH_DLS_PTBL_HEADER_SIZE) { SET_SIZE_ERROR(riff, -1, err); return (NULL); } if(!ipatch_file_buf_load(riff->handle, IPATCH_DLS_PTBL_HEADER_SIZE, err)) { return (NULL); } header_size = ipatch_file_buf_read_u32(riff->handle); if(header_size < IPATCH_DLS_PTBL_HEADER_SIZE || header_size & 1) { /* make sure art header size is sane */ SET_DATA_ERROR(riff, -1, err); return (NULL); } /* check for header expansion */ if(header_size > IPATCH_DLS_PTBL_HEADER_SIZE) { /* skip expansion data */ if(!ipatch_file_seek(riff->handle, header_size - IPATCH_DLS_PTBL_HEADER_SIZE, G_SEEK_CUR, err)) { return (NULL); } /* load cue count (last field before cue offsets) */ if(!ipatch_file_buf_load(riff->handle, 4, err)) { return (NULL); } } count = ipatch_file_buf_read_u32(riff->handle); if(chunk->size != header_size + count * IPATCH_DLS_POOLCUE_SIZE) { /* make sure pool cue count is sane */ SET_SIZE_ERROR(riff, -1, err); return (NULL); } if(!count) { return (NULL); /* no pool table? */ } cuep = g_malloc(chunk->size - header_size); /* load pool cue offsets */ if(!ipatch_file_read(riff->handle, cuep, chunk->size - header_size, err)) { g_free(cuep); return (NULL); } *size = count; /* do endian swap on cue offsets if needed */ if(IPATCH_RIFF_NEED_SWAP(riff)) { tmpcuep = cuep; while(count-- > 0) { *tmpcuep = GUINT32_SWAP_LE_BE(*tmpcuep); tmpcuep++; } } return (cuep); } /** * ipatch_dls_load_dlid: * @riff: Riff parser * @dlid: (out): Location to store 16 byte DLSID * @err: Location to store error info or %NULL * * Load a DLSID from the current position in a riff object. This is a * 16 byte unique ID used for tracking data changes. The "dlid" header * should have already been loaded. * * Returns: %TRUE on success, %FALSE otherwise */ static gboolean ipatch_dls_load_dlid(IpatchRiff *riff, guint8 *dlid, GError **err) { IpatchRiffChunk *chunk; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(dlid != NULL, FALSE); g_return_val_if_fail(!err || !*err, FALSE); chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size != 16) { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } if(!ipatch_file_read(riff->handle, dlid, 16, err)) { return (FALSE); } return (TRUE); } /** * ipatch_gig_load_sample_info: * @riff: RIFF parser * @info: DLS sample info to load data into * @err: Location to store error info or %NULL * * Load Gig sample info ("smpl" chunk) from current position in file assigned * to @riff (chunk header should already be loaded). * * Returns: %TRUE on success, %FALSE on error (in which case @err may be set). */ static gboolean ipatch_gig_load_sample_info(IpatchRiff *riff, IpatchDLS2SampleInfo *info, GError **err) { IpatchRiffChunk *chunk; guint32 loop_count; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(info != NULL, FALSE); g_return_val_if_fail(!err || !*err, FALSE); chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size != IPATCH_GIG_SMPL_SIZE) { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } if(!ipatch_file_buf_load(riff->handle, IPATCH_GIG_SMPL_SIZE, err)) { return (FALSE); } ipatch_file_buf_read_u32(riff->handle); /* manufacturer */ ipatch_file_buf_read_u32(riff->handle); /* product */ ipatch_file_buf_read_u32(riff->handle); /* sample period in nanoseconds */ info->root_note = (guint8)ipatch_file_buf_read_u32(riff->handle); /* FIXME - Is this an unsigned 32 bit fraction of a semitone? */ info->fine_tune = (guint16)ipatch_file_buf_read_u32(riff->handle); ipatch_file_buf_read_u32(riff->handle); /* SMPTE format */ ipatch_file_buf_read_u32(riff->handle); /* SMPTE offset */ loop_count = ipatch_file_buf_read_u32(riff->handle); ipatch_file_buf_read_u32(riff->handle); /* manufBytes */ ipatch_file_buf_read_u32(riff->handle); /* loop ID */ if(loop_count > 0) /* we only use 1 loop if it exists */ { ipatch_file_buf_read_u32(riff->handle); /* loop type - FIXME! */ info->options |= IPATCH_SAMPLE_LOOP_STANDARD; info->loop_start = ipatch_file_buf_read_u32(riff->handle); info->loop_end = ipatch_file_buf_read_u32(riff->handle); } return (TRUE); } /** * ipatch_gig_load_dimension_info: * @riff: RIFF parser * @region: Region to load data into * @err: Location to store an error or %NULL * * Load Gigasampler dimension info ("3lnk" chunk), from the current position * in the file assigned to @riff (chunk header should already be loaded). * * NOTE: Sample pool table indexes are stored in the sample pointer of * sub regions. These indexes should be fixed up or cleared before the * @region is freed, otherwise bad things will happen. * * Returns: %TRUE on success, %FALSE on error (in which case @err may be set). */ static gboolean ipatch_gig_load_dimension_info(IpatchRiff *riff, IpatchGigRegion *region, GError **err) { IpatchRiffChunk *chunk; int count, temp_count, split_count; guint8 type, c; int i; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(IPATCH_IS_GIG_REGION(region), FALSE); g_return_val_if_fail(!err || !*err, FALSE); chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size != IPATCH_GIG_3LNK_SIZE) { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } if(!ipatch_file_buf_load(riff->handle, IPATCH_GIG_3LNK_SIZE, err)) { return (FALSE); } count = ipatch_file_buf_read_u32(riff->handle); /* count of sub regions */ if(count < 1 || count > 32) /* should be between 1 and 32 sub regions */ { SET_DATA_ERROR(riff, -1, err); return (FALSE); } /* calculate number of used split bits */ temp_count = count; split_count = 0; while(!(temp_count & 1)) { temp_count >>= 1; split_count++; } if(temp_count != 1) /* make sure count is a power of 2 */ { SET_DATA_ERROR(riff, -1, err); return (FALSE); } while(split_count > 0) { type = ipatch_file_buf_read_u8(riff->handle); /* type of dimension */ if(type > IPATCH_GIG_DIMENSION_TYPE_MAX) { DEBUG_DLS("Unknown GigaSampler dimension type '0x%x'", type); } c = ipatch_file_buf_read_u8(riff->handle); /* split bit count */ ipatch_file_buf_skip(riff->handle, 6); /* FIXME - skip ignored stuff */ ipatch_gig_region_new_dimension(region, type, c); split_count -= c; } if(split_count != 0) /* a split bit count is messed up? */ { SET_DATA_ERROR(riff, -1, err); return (FALSE); } /* "seek" to sample cue list */ ipatch_file_buf_seek(riff->handle, 44, G_SEEK_SET); /* store sample indexes to sub region sample pointer fields (fixup later) */ for(i = 0; i < region->sub_region_count; i++) region->sub_regions[i]->sample = GUINT_TO_POINTER(ipatch_file_buf_read_u32(riff->handle)); return (TRUE); } /** * ipatch_gig_load_dimension_names: * @riff: RIFF parser * @region: Gig region to load names into * @err: Location to store error info or %NULL * * Loads Gigasampler dimension names from the current position in the file * assigned to @riff. The "3dnl" chunk header should already have been * loaded. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ static gboolean ipatch_gig_load_dimension_names(IpatchRiff *riff, IpatchGigRegion *region, GError **err) { IpatchRiffChunk *chunk; char name[256]; /* just use a static buffer for name */ int i, size; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(IPATCH_IS_GIG_REGION(region), FALSE); g_return_val_if_fail(!err || !*err, FALSE); chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size == 0) { return (TRUE); /* no dimension names */ } while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_SUB && strncmp(chunk->idstr, "nam", 3) == 0 && chunk->size > 0) { /* 4th char of FOURCC is dimension index */ i = chunk->idstr[3] - '0'; if(i >= 0 && i < region->dimension_count) { size = MIN(chunk->size, 255); if(!ipatch_file_read(riff->handle, name, size, err)) { return (FALSE); } name[size] = '\0'; if(name[0] != '\0') { g_object_set(region->dimensions[i], "name", name, NULL); } } } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } return (ipatch_riff_get_error(riff, NULL)); } /** * ipatch_gig_load_group_names: * @riff: Riff parser * @name_list: (out) (element-type char*): List to add names to * @err: Location to store error info or %NULL * * Load a '3gri' sample group name chunk into a GSList of strings. The * 3gri chunk header should have already been loaded. * * Returns: %TRUE on success, %FALSE otherwise */ static gboolean ipatch_gig_load_group_names(IpatchRiff *riff, GSList **name_list, GError **err) { IpatchRiffChunk *chunk; char buf[65], *name; int size; GSList *p; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(name_list != NULL, FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* no chunks? - return */ if(!(chunk = ipatch_riff_read_chunk(riff, err))) { return (ipatch_riff_get_error(riff, NULL)); } /* not a '3gnl' chunk? - return */ if(chunk->type != IPATCH_RIFF_CHUNK_LIST || chunk->id != IPATCH_GIG_FOURCC_3GNL) { if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } return (TRUE); } /* loop over 3gnm chunks */ while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_SUB && chunk->id == IPATCH_GIG_FOURCC_3GNM) { size = MIN(chunk->size, sizeof(buf) - 1); if(!ipatch_file_read(riff->handle, &buf, size, err)) { goto err; } buf[sizeof(buf) - 1] = '\0'; size = strlen(buf); if(size > 0) { name = g_memdup(buf, size + 1); *name_list = g_slist_append(*name_list, name); } } if(!ipatch_riff_end_chunk(riff, err)) { goto err; } } /* make sure no errors occured */ if(!ipatch_riff_get_error(riff, NULL)) { return (FALSE); } if(!ipatch_riff_end_chunk(riff, err)) { goto err; } return (TRUE); err: /* free any existing names */ p = *name_list; while(p) { g_free(p->data); p = g_slist_delete_link(p, p); } *name_list = NULL; return (FALSE); } libinstpatch-1.1.6/libinstpatch/IpatchDLSReader.h000066400000000000000000000060271400263525300217430ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_DLS_READER_H__ #define __IPATCH_DLS_READER_H__ #include #include #include #include #include #include #include #include #include typedef struct _IpatchDLSReader IpatchDLSReader; /* private structure */ typedef struct _IpatchDLSReaderClass IpatchDLSReaderClass; #define IPATCH_TYPE_DLS_READER (ipatch_dls_reader_get_type ()) #define IPATCH_DLS_READER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_DLS_READER, \ IpatchDLSReader)) #define IPATCH_DLS_READER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_DLS_READER, \ IpatchDLSReaderClass)) #define IPATCH_IS_DLS_READER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_DLS_READER)) #define IPATCH_IS_DLS_READER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_DLS_READER)) /* error domain for DLS Reader */ #define IPATCH_DLS_READER_ERROR ipatch_dls_reader_error_quark() typedef enum { /* this error is returned if a file originally thought to be a plain DLS file turns out to be a GigaSampler file, in which case loading should be restarted in GigaSampler mode */ IPATCH_DLS_READER_ERROR_GIG } IpatchDLSReaderError; /* DLS reader object */ struct _IpatchDLSReader { IpatchRiff parent_instance; /* derived from IpatchRiff */ IpatchDLS2 *dls; /* DLS or GigaSampler object to load file into */ gboolean is_gig; /* set if dls is a GigaSampler object */ gboolean needs_fixup; /* set if regions in dls need fixup */ GHashTable *wave_hash; /* wave chunk file offset -> sample hash */ guint32 *pool_table; /* wave pool table (index -> wave chunk file offset) */ guint pool_table_size; /* size of pool table (in cue entries) */ }; /* DLS reader class */ struct _IpatchDLSReaderClass { IpatchRiffClass parent_class; }; GType ipatch_dls_reader_get_type(void); IpatchDLSReader *ipatch_dls_reader_new(IpatchFileHandle *handle); IpatchDLS2 *ipatch_dls_reader_load(IpatchDLSReader *reader, GError **err); #endif libinstpatch-1.1.6/libinstpatch/IpatchDLSWriter.c000066400000000000000000001634641400263525300220210ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchDLSWriter * @short_description: DLS instrument file writer * @see_also: #IpatchDLS * @stability: Stable * * Writes a DLS instrument object tree (#IpatchDLS) to a DLS file. */ #include #include #include "IpatchDLSWriter.h" #include "IpatchDLSFile.h" #include "IpatchDLSFile_priv.h" #include "IpatchDLS2Region.h" #include "IpatchDLS2Sample.h" #include "IpatchGig.h" #include "IpatchGigFile.h" #include "IpatchGigFile_priv.h" #include "IpatchGigRegion.h" #include "IpatchGigInst.h" #include "IpatchGigSample.h" #include "IpatchItem.h" #include "IpatchSampleData.h" #include "IpatchSampleStoreFile.h" #include "IpatchSample.h" #include "ipatch_priv.h" #include "i18n.h" static void ipatch_dls_writer_finalize(GObject *object); static gboolean ipatch_dls_write_level_0(IpatchDLSWriter *writer, GError **err); static gboolean dls_write_info(IpatchDLSWriter *writer, GSList *info_list, GError **err); static IpatchDLS2InfoBag *find_info_by_id(GSList *info_list, guint32 id); static gboolean gig_write_file_info(IpatchDLSWriter *writer, GSList *info_list, GError **err); static gboolean gig_write_name_info(IpatchDLSWriter *writer, GSList *info_list, GError **err); static gboolean dls_write_inst_list(IpatchDLSWriter *writer, GError **err); static gboolean dls_write_region_list(IpatchDLSWriter *writer, IpatchDLS2Inst *inst, GError **err); static gboolean gig_write_region_list(IpatchDLSWriter *writer, IpatchGigInst *giginst, GError **err); static gboolean dls_write_art_list(IpatchDLSWriter *writer, GSList *conn_list, GError **err); static gboolean dls_write_region_header(IpatchDLSWriter *writer, IpatchDLS2Region *region, GError **err); static gboolean gig_write_region_header(IpatchDLSWriter *writer, IpatchGigRegion *region, GError **err); static gboolean dls_write_wave_link(IpatchDLSWriter *writer, IpatchDLS2Region *region, GError **err); static gboolean gig_write_wave_link(IpatchDLSWriter *writer, IpatchGigRegion *region, GError **err); static gboolean dls_write_sample_info(IpatchDLSWriter *writer, IpatchDLS2SampleInfo *info, GError **err); static gboolean dls_write_sample_format(IpatchDLSWriter *writer, IpatchDLS2Sample *sample, GError **err); static gboolean dls_reserve_pool_table(IpatchDLSWriter *writer, GError **err); static gboolean dls_fixup_pool_table(IpatchDLSWriter *writer, GError **err); static gboolean dls_write_wave_pool(IpatchDLSWriter *writer, GError **err); static gboolean dls_write_dlid(IpatchDLSWriter *writer, guint8 *dlid, GError **err); static gboolean gig_write_sub_regions(IpatchDLSWriter *writer, IpatchGigRegion *region, GError **err); static gboolean gig_write_dimension_names(IpatchDLSWriter *writer, IpatchGigRegion *region, GError **err); static gboolean gig_write_sample_info(IpatchDLSWriter *writer, IpatchDLS2SampleInfo *info, int rate, GError **err); static gboolean gig_write_dimension_info(IpatchDLSWriter *writer, IpatchGigRegion *region, GError **err); static gboolean gig_write_group_names(IpatchDLSWriter *writer, GError **err); G_DEFINE_TYPE(IpatchDLSWriter, ipatch_dls_writer, IPATCH_TYPE_RIFF) static void ipatch_dls_writer_class_init(IpatchDLSWriterClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->finalize = ipatch_dls_writer_finalize; } static void ipatch_dls_writer_init(IpatchDLSWriter *writer) { writer->sample_hash = g_hash_table_new(NULL, NULL); } static void ipatch_dls_writer_finalize(GObject *object) { IpatchDLSWriter *writer = IPATCH_DLS_WRITER(object); if(writer->orig_dls) { g_object_unref(writer->orig_dls); } if(writer->dls) { g_object_unref(writer->dls); } g_hash_table_destroy(writer->sample_hash); if(writer->sample_ofstbl) { g_free(writer->sample_ofstbl); } if(writer->sample_postbl) { g_free(writer->sample_postbl); } if(writer->store_list) { g_object_unref(writer->store_list); } if(G_OBJECT_CLASS(ipatch_dls_writer_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_dls_writer_parent_class)->finalize(object); } } /** * ipatch_dls_writer_new: * @handle: DLS file handle to save to or %NULL to set later, taken over by * writer object and will be closed on finalize. * @dls: DLS object to save or %NULL to set later * * Create a new DLS file writer. * * Returns: The new DLS writer */ IpatchDLSWriter * ipatch_dls_writer_new(IpatchFileHandle *handle, IpatchDLS2 *dls) { IpatchDLSWriter *writer; g_return_val_if_fail(!handle || IPATCH_IS_DLS_FILE(handle->file), NULL); g_return_val_if_fail(!dls || IPATCH_IS_DLS2(dls), NULL); writer = g_object_new(IPATCH_TYPE_DLS_WRITER, NULL); if(handle) { ipatch_dls_writer_set_file_handle(writer, handle); } if(dls) { ipatch_dls_writer_set_patch(writer, dls); } return (writer); } /** * ipatch_dls_writer_set_patch: * @writer: DLS writer object * @dls: DLS patch to save * * Set the DLS patch object to save with a DLS writer. */ void ipatch_dls_writer_set_patch(IpatchDLSWriter *writer, IpatchDLS2 *dls) { g_return_if_fail(IPATCH_IS_DLS_WRITER(writer)); g_return_if_fail(IPATCH_IS_DLS2(dls)); if(writer->orig_dls) { g_object_unref(writer->orig_dls); } writer->orig_dls = g_object_ref(dls); } /** * ipatch_dls_writer_set_file_handle: * @writer: DLS writer object * @handle: DLS file handle * * Set the DLS file handle of a DLS writer. A convenience function, since * ipatch_riff_set_file_handle() could also be used. */ void ipatch_dls_writer_set_file_handle(IpatchDLSWriter *writer, IpatchFileHandle *handle) { g_return_if_fail(IPATCH_IS_DLS_WRITER(writer)); g_return_if_fail(handle && IPATCH_IS_DLS_FILE(handle->file)); ipatch_riff_set_file_handle(IPATCH_RIFF(writer), handle); } /** * ipatch_dls_writer_save: * @writer: DLS writer object * @err: Location to store error info or %NULL * * Write a DLS or GigaSampler object to a file. * * Returns: %TRUE on success, %FALSE on error */ gboolean ipatch_dls_writer_save(IpatchDLSWriter *writer, GError **err) { IpatchRiff *riff; IpatchItem *item; g_return_val_if_fail(IPATCH_IS_DLS_WRITER(writer), FALSE); g_return_val_if_fail(!err || !*err, FALSE); g_return_val_if_fail(writer->orig_dls != NULL, FALSE); if(writer->dls) { g_object_unref(writer->dls); /* shouldn't be set, but.. */ } /* are we writing a GigaSampler file? */ if(IPATCH_IS_GIG(writer->orig_dls)) { writer->is_gig = TRUE; } /* duplicate for save, so we can be multi-thread friendly :) ++ref new duplicate object */ item = ipatch_item_duplicate(IPATCH_ITEM(writer->orig_dls)); g_return_val_if_fail(item != NULL, FALSE); writer->dls = IPATCH_DLS2(item); riff = IPATCH_RIFF(writer); /* - Toplevel DLS RIFF chunk */ if(!ipatch_riff_write_chunk(riff, IPATCH_RIFF_CHUNK_RIFF, IPATCH_DLS_FOURCC_DLS, err)) { return (FALSE); } if(!ipatch_dls_write_level_0(writer, err)) { goto err; } if(!ipatch_riff_close_chunk(riff, -1, err)) { goto err; } /* reset flag "changed" to false. set flag "saved" to true */ g_object_set (writer->orig_dls, "changed", FALSE, /* file and object are in sync */ "saved", TRUE, /* has now been saved */ NULL); /* */ return (TRUE); err: g_object_unref(writer->dls); writer->dls = NULL; return (FALSE); } /** * ipatch_dls_writer_create_stores: * @writer: SoundFont writer object * * Create sample stores and add them to applicable #IpatchSampleData objects and return object list. * This function can be called multiple times, additional calls will return the same list. * * Returns: (transfer full): List of sample stores which the caller owns a reference to or %NULL * * Since: 1.1.0 */ IpatchList * ipatch_dls_writer_create_stores(IpatchDLSWriter *writer) { guint sample_index; IpatchSample *newstore; IpatchFile *save_file; IpatchDLS2Sample *sample; IpatchIter iter; IpatchList *list; int rate, format; guint size, pos; g_return_val_if_fail(writer->dls != NULL, NULL); // Return existing store list (if this function has been called before) if(writer->store_list) { return (g_object_ref(writer->store_list)); // ++ ref for caller } save_file = IPATCH_RIFF(writer)->handle->file; if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->dls), &iter, IPATCH_TYPE_DLS2_SAMPLE)) { return (NULL); } list = ipatch_list_new(); // ++ ref list /* traverse samples */ for(sample = ipatch_dls2_sample_first(&iter); sample; sample = ipatch_dls2_sample_next(&iter)) { sample_index = GPOINTER_TO_UINT(g_hash_table_lookup(writer->sample_hash, sample)); /* Hash_value should never be NULL, but.. */ if(!sample_index) { continue; } pos = writer->sample_postbl[sample_index - 1]; // sample index is +1 to catch NULL g_object_get(sample, "sample-format", &format, "sample-size", &size, "sample-rate", &rate, NULL); newstore = ipatch_sample_store_file_new(save_file, pos); g_object_set(newstore, "sample-format", format, "sample-size", size, "sample-rate", rate, NULL); ipatch_sample_data_add(sample->sample_data, (IpatchSampleStore *)newstore); list->items = g_list_prepend(list->items, newstore); // !! list takes over reference } writer->store_list = g_object_ref(list); // ++ ref for writer object return (list); // !! caller takes over reference } static gboolean ipatch_dls_write_level_0(IpatchDLSWriter *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchIter iter; IpatchDLS2Sample *sample; guint index; /* write info list */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_INFO, err)) { return (FALSE); } if(!writer->is_gig) /* DLS file? */ { if(!dls_write_info(writer, writer->dls->info, err)) { return (FALSE); } } else /* gig file */ { if(!gig_write_file_info(writer, writer->dls->info, err)) { return (FALSE); } } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* save file version if its set */ if(ipatch_item_get_flags(writer->dls) & IPATCH_DLS2_VERSION_SET) { if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_VERS, err)) { return (FALSE); } if(!ipatch_file_write_u32(riff->handle, writer->dls->ms_version, err)) { return (FALSE); } if(!ipatch_file_write_u32(riff->handle, writer->dls->ls_version, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } } /* collection header (instrument count) */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_COLH, err)) { return (FALSE); } if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->dls), &iter, IPATCH_TYPE_DLS2_INST)) { return (FALSE); } if(!ipatch_file_write_u32(riff->handle, ipatch_iter_count(&iter), err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* write DLSID if its set */ if(writer->dls->dlid) if(!dls_write_dlid(writer, writer->dls->dlid, err)) { return (FALSE); } /* create hash of samples -> indexes */ if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->dls), &iter, IPATCH_TYPE_DLS2_SAMPLE)) { return (FALSE); } sample = ipatch_dls2_sample_first(&iter); index = 1; /* index + 1 to catch NULL in hash table */ while(sample) { g_hash_table_insert(writer->sample_hash, sample, GUINT_TO_POINTER(index)); sample = ipatch_dls2_sample_next(&iter); index++; } writer->sample_count = index - 1; /* count of samples */ /* allocate sample offset and sample data position tables */ writer->sample_ofstbl = g_malloc0(writer->sample_count * 4); writer->sample_postbl = g_malloc0(writer->sample_count * 4); /* write instrument list */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_LINS, err)) { return (FALSE); } if(!dls_write_inst_list(writer, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* GigaSampler sample group name list */ if(writer->is_gig) if(!gig_write_group_names(writer, err)) { return (FALSE); } /* reserve pool table (sample mappings) chunk */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_PTBL, err)) { return (FALSE); } if(!dls_reserve_pool_table(writer, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* write wave pool list (all samples) */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_WVPL, err)) { return (FALSE); } if(!dls_write_wave_pool(writer, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* fixup the pool table */ if(!dls_fixup_pool_table(writer, err)) { return (FALSE); } /* FIXME: IPATCH_GIG_FOURCC_EINF - GigaSampler unknown */ return (TRUE); } static gboolean dls_write_info(IpatchDLSWriter *writer, GSList *info_list, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchDLS2InfoBag *bag; GSList *p; p = info_list; while(p) { bag = (IpatchDLS2InfoBag *)(p->data); if(!ipatch_riff_write_sub_chunk(riff, bag->fourcc, err)) { return (FALSE); } if(!ipatch_file_write(riff->handle, bag->value, /* write info str */ strlen(bag->value) + 1, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } p = g_slist_next(p); } return (TRUE); } static IpatchDLS2InfoBag * find_info_by_id(GSList *info_list, guint32 id) { IpatchDLS2InfoBag *bag; GSList *p; p = info_list; while(p) { bag = (IpatchDLS2InfoBag *)(p->data); if(bag->fourcc == id) { break; } p = g_slist_next(p); } if(p) { return (bag); } else { return (NULL); } } /* GigaSampler file info write function */ static gboolean gig_write_file_info(IpatchDLSWriter *writer, GSList *info_list, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchDLS2InfoBag *bag; char *buf; int i, size; guint32 order[] = { IPATCH_DLS_FOURCC_IARL, IPATCH_DLS_FOURCC_IART, IPATCH_DLS_FOURCC_ICMS, IPATCH_DLS_FOURCC_ICMT, IPATCH_DLS_FOURCC_ICOP, IPATCH_DLS_FOURCC_ICRD, IPATCH_DLS_FOURCC_IENG, IPATCH_DLS_FOURCC_IGNR, IPATCH_DLS_FOURCC_IKEY, IPATCH_DLS_FOURCC_IMED, IPATCH_DLS_FOURCC_INAM, IPATCH_DLS_FOURCC_IPRD, IPATCH_DLS_FOURCC_ISBJ, IPATCH_DLS_FOURCC_ISFT, IPATCH_DLS_FOURCC_ISRC, IPATCH_DLS_FOURCC_ISRF, IPATCH_DLS_FOURCC_ITCH }; buf = g_malloc(1024); /* max size is comment field */ for(i = 0; i < G_N_ELEMENTS(order); i++) { if(order[i] == IPATCH_DLS_FOURCC_IARL) { size = IPATCH_GIG_IARL_INFO_SIZE; } else if(order[i] == IPATCH_DLS_FOURCC_ICMT) { size = IPATCH_GIG_ICMT_INFO_SIZE; } else { size = IPATCH_GIG_MOST_INFO_SIZE; } /* blank it first, IARL filled with spaces */ memset(buf, (order[i] != IPATCH_DLS_FOURCC_IARL) ? 0 : ' ', size); bag = find_info_by_id(info_list, order[i]); if(bag) { strncpy(buf, bag->value, size - 1); } if(!ipatch_riff_write_sub_chunk(riff, order[i], err)) { goto err; } if(!ipatch_file_write(riff->handle, buf, size, err)) { goto err; } if(!ipatch_riff_close_chunk(riff, -1, err)) { goto err; } } return (TRUE); err: g_free(buf); return (FALSE); } /* write GigaSampler name info for instruments or samples */ static gboolean gig_write_name_info(IpatchDLSWriter *writer, GSList *info_list, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchDLS2InfoBag *bag; char buf[IPATCH_GIG_ITEM_INAM_SIZE]; memset(buf, 0, sizeof(buf)); bag = find_info_by_id(info_list, IPATCH_DLS_FOURCC_INAM); if(bag) { strncpy(buf, bag->value, sizeof(buf) - 1); } if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_INAM, err)) { return (FALSE); } if(!ipatch_file_write(riff->handle, buf, sizeof(buf), err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } return (TRUE); } static gboolean dls_write_inst_list(IpatchDLSWriter *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchDLS2Inst *inst; IpatchIter iter, region_iter; guint32 uint; gboolean retval; if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->dls), &iter, IPATCH_TYPE_DLS2_INST)) { return (FALSE); } inst = ipatch_dls2_inst_first(&iter); while(inst) /* loop over instruments */ { /* - Instrument chunk */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_INS, err)) { return (FALSE); } /* - Info list */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_INFO, err)) { return (FALSE); } if(writer->is_gig) { if(!gig_write_name_info(writer, inst->info, err)) { return (FALSE); } /* - Write ISFT info value - FIXME (write libInstPatch?) */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_ISFT, err)) { return (FALSE); } if(!ipatch_file_write(riff->handle, IPATCH_GIG_INST_ISFT_VAL, strlen(IPATCH_GIG_INST_ISFT_VAL), err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ } else if(!dls_write_info(writer, inst->info, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ /* write DLSID if its set */ if(inst->dlid) if(!dls_write_dlid(writer, inst->dlid, err)) { return (FALSE); } /* - write instrument header chunk */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_INSH, err)) { return (FALSE); } /* region count */ if(!writer->is_gig) { if(!ipatch_container_init_iter((IpatchContainer *)inst, ®ion_iter, IPATCH_TYPE_DLS2_REGION)) { return (FALSE); } } else { if(!ipatch_container_init_iter((IpatchContainer *)inst, ®ion_iter, IPATCH_TYPE_GIG_REGION)) { return (FALSE); } } ipatch_file_buf_write_u32(riff->handle, ipatch_iter_count(®ion_iter)); uint = inst->bank | ((ipatch_item_get_flags(inst) & IPATCH_DLS2_INST_PERCUSSION) ? IPATCH_DLS_INSH_BANK_PERCUSSION : 0); ipatch_file_buf_write_u32(riff->handle, uint); ipatch_file_buf_write_u32(riff->handle, inst->program); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ /* - region list */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_LRGN, err)) { return (FALSE); } if(!writer->is_gig) { retval = dls_write_region_list(writer, inst, err); } else { retval = gig_write_region_list(writer, IPATCH_GIG_INST(inst), err); } if(!retval) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ /* - Global DLS2 articulator list */ if(inst->conns) { if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_LAR2, err)) { return (FALSE); } if(!dls_write_art_list(writer, inst->conns, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } } /* */ /* FIXME - Global DLS1 articulators? */ /* if GigaSampler - write 3ewg in lart list (global region params) */ if(writer->is_gig) { /* */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_LART, err)) { return (FALSE); } /* <3ewg> - GigaSampler 3ewg chunk */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_GIG_FOURCC_3EWG, err)) { return (FALSE); } if(!ipatch_file_write(riff->handle, &(IPATCH_GIG_INST(inst)->chunk_3ewg), IPATCH_GIG_3EWG_SIZE, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ inst = ipatch_dls2_inst_next(&iter); /* next instrument */ } /* while (p) - instrument loop */ return (TRUE); } static gboolean dls_write_region_list(IpatchDLSWriter *writer, IpatchDLS2Inst *inst, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchDLS2Region *region; IpatchIter iter; if(!ipatch_container_init_iter((IpatchContainer *)inst, &iter, IPATCH_TYPE_DLS2_REGION)) { return (FALSE); } region = ipatch_dls2_region_first(&iter); while(region) /* loop over regions */ { /* FIXME: DLS1 chunks? */ /* - DLS2 region */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_RGN2, err)) { return (FALSE); } /* FIXME: - conditional chunk */ if(region->info) { /* - Region info */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_INFO, err)) { return (FALSE); } if(!dls_write_info(writer, region->info, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } } /* */ /* - Region header */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_RGNH, err)) { return (FALSE); } if(!dls_write_region_header(writer, region, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ if(region->sample_info) { /* - Global sample info override */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_WSMP, err)) { return (FALSE); } if(!dls_write_sample_info(writer, region->sample_info, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } } /* */ /* - Wave link */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_WLNK, err)) { return (FALSE); } if(!dls_write_wave_link(writer, region, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ /* FIXME: - DLS1 articulators? */ if(region->conns) { /* - DLS2 articulators */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_LAR2, err)) { return (FALSE); } if(!dls_write_art_list(writer, region->conns, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } } /* */ if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ region = ipatch_dls2_region_next(&iter); } return (TRUE); } static gboolean gig_write_region_list(IpatchDLSWriter *writer, IpatchGigInst *giginst, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchDLS2Inst *inst = IPATCH_DLS2_INST(giginst); IpatchGigRegion *region; IpatchGigSubRegion *subregion; IpatchIter iter; if(!ipatch_container_init_iter((IpatchContainer *)inst, &iter, IPATCH_TYPE_GIG_REGION)) { return (FALSE); } region = ipatch_gig_region_first(&iter); while(region) /* loop over regions */ { /* - GigaSampler region */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_RGN, err)) { return (FALSE); } if(region->info) { /* - Region info */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_INFO, err)) { return (FALSE); } if(!dls_write_info(writer, region->info, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } } /* */ /* - Region header */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_RGNH, err)) { return (FALSE); } if(!gig_write_region_header(writer, region, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ if(region->sub_region_count > 0) { subregion = region->sub_regions[0]; } else { subregion = NULL; } /* - This is somewhat of a dummy WSMP chunk. */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_WSMP, err)) { return (FALSE); } if(!dls_write_sample_info(writer, subregion ? subregion->sample_info : NULL, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ /* - Wave link */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_WLNK, err)) { return (FALSE); } if(!gig_write_wave_link(writer, region, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ /* <3LNK> - GigaSampler dimension info */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_GIG_FOURCC_3LNK, err)) { return (FALSE); } if(!gig_write_dimension_info(writer, region, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ /* <3PRG> - GigaSampler regions */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_GIG_FOURCC_3PRG, err)) { return (FALSE); } if(!gig_write_sub_regions(writer, region, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ /* <3DNL> - GigaSampler dimension names */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_GIG_FOURCC_3DNL, err)) { return (FALSE); } if(!gig_write_dimension_names(writer, region, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ /* FIXME: <3DDP> - what is it? (we preserve it for now) */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_GIG_FOURCC_3DDP, err)) { return (FALSE); } if(!ipatch_file_write(riff->handle, ®ion->chunk_3ddp, IPATCH_GIG_3DDP_SIZE, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ region = ipatch_gig_region_next(&iter); } return (TRUE); } static gboolean dls_write_art_list(IpatchDLSWriter *writer, GSList *conn_list, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchDLS2Conn *conn; GSList *p; /* <3EWG> - Gig region global params handled elsewhere */ /* FIXME: - Conditional chunk */ /* FIXME: - DLS1 articulators? */ if(!conn_list) { return (TRUE); /* no connections? */ } /* */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_ART2, err)) { return (FALSE); } /* write articulator header size */ ipatch_file_buf_write_u32(riff->handle, IPATCH_DLS_ART_HEADER_SIZE); /* FIXME: Preserve header expansion? */ /* write connection count */ ipatch_file_buf_write_u32(riff->handle, g_slist_length(conn_list)); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } p = conn_list; while(p) /* write connection blocks */ { conn = (IpatchDLS2Conn *)(p->data); ipatch_file_buf_write_u16(riff->handle, conn->src); ipatch_file_buf_write_u16(riff->handle, conn->ctrlsrc); ipatch_file_buf_write_u16(riff->handle, conn->dest); ipatch_file_buf_write_u16(riff->handle, conn->trans); ipatch_file_buf_write_s32(riff->handle, conn->scale); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } p = g_slist_next(p); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ return (TRUE); } static gboolean dls_write_region_header(IpatchDLSWriter *writer, IpatchDLS2Region *region, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); guint16 options = 0; ipatch_file_buf_write_u16(riff->handle, region->note_range_low); ipatch_file_buf_write_u16(riff->handle, region->note_range_high); ipatch_file_buf_write_u16(riff->handle, region->velocity_range_low); ipatch_file_buf_write_u16(riff->handle, region->velocity_range_high); if(ipatch_item_get_flags(region) & IPATCH_DLS2_REGION_SELF_NON_EXCLUSIVE) { options |= IPATCH_DLS_RGNH_OPTION_SELF_NON_EXCLUSIVE; } ipatch_file_buf_write_u16(riff->handle, options); ipatch_file_buf_write_u16(riff->handle, region->key_group); if(region->layer_group != 0) /* optional layer field? */ { ipatch_file_buf_write_u16(riff->handle, region->layer_group); } if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } static gboolean gig_write_region_header(IpatchDLSWriter *writer, IpatchGigRegion *region, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); guint16 options = 0; ipatch_file_buf_write_u16(riff->handle, region->note_range_low); ipatch_file_buf_write_u16(riff->handle, region->note_range_high); ipatch_file_buf_write_u16(riff->handle, region->velocity_range_low); ipatch_file_buf_write_u16(riff->handle, region->velocity_range_high); if(ipatch_item_get_flags(region) & IPATCH_GIG_REGION_SELF_NON_EXCLUSIVE) { options |= IPATCH_DLS_RGNH_OPTION_SELF_NON_EXCLUSIVE; } ipatch_file_buf_write_u16(riff->handle, options); ipatch_file_buf_write_u16(riff->handle, region->key_group); if(region->layer_group != 0) /* optional layer field? */ { ipatch_file_buf_write_u16(riff->handle, region->layer_group); } if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } static gboolean dls_write_wave_link(IpatchDLSWriter *writer, IpatchDLS2Region *region, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); guint16 options = 0; guint flags; guint sample_index = 0; flags = ipatch_item_get_flags(region); if(flags & IPATCH_DLS2_REGION_PHASE_MASTER) { options |= IPATCH_DLS_WLNK_PHASE_MASTER; } if(flags & IPATCH_DLS2_REGION_MULTI_CHANNEL) { options |= IPATCH_DLS_WLNK_MULTI_CHANNEL; } ipatch_file_buf_write_u16(riff->handle, options); ipatch_file_buf_write_u16(riff->handle, region->phase_group); ipatch_file_buf_write_u32(riff->handle, region->channel); /* get index of sample (index + 1 actually) */ sample_index = GPOINTER_TO_UINT(g_hash_table_lookup(writer->sample_hash, region->sample)); g_return_val_if_fail(sample_index != 0, FALSE); /* write sample index (subtract 1 since we store it +1 to catch NULL) */ ipatch_file_buf_write_u32(riff->handle, sample_index - 1); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } static gboolean gig_write_wave_link(IpatchDLSWriter *writer, IpatchGigRegion *region, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchGigSubRegion *subregion; guint16 options = 0; guint flags; guint sample_index = 0; flags = ipatch_item_get_flags(region); if(flags & IPATCH_GIG_REGION_PHASE_MASTER) { options |= IPATCH_DLS_WLNK_PHASE_MASTER; } if(flags & IPATCH_GIG_REGION_MULTI_CHANNEL) { options |= IPATCH_DLS_WLNK_MULTI_CHANNEL; } ipatch_file_buf_write_u16(riff->handle, options); ipatch_file_buf_write_u16(riff->handle, region->phase_group); ipatch_file_buf_write_u32(riff->handle, region->channel); if(region->sub_region_count > 0) { subregion = region->sub_regions[0]; sample_index = GPOINTER_TO_UINT (g_hash_table_lookup(writer->sample_hash, subregion->sample)); } g_return_val_if_fail(sample_index != 0, FALSE); /* write sample index (subtract 1 since we store it +1 to catch NULL) */ ipatch_file_buf_write_u32(riff->handle, sample_index - 1); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } /* writes sample info, @info may be NULL in which case defaults are used */ static gboolean dls_write_sample_info(IpatchDLSWriter *writer, IpatchDLS2SampleInfo *info, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchDLS2SampleInfo definfo = IPATCH_DLS2_SAMPLE_INFO_INIT; guint loop_type, saveloop_type; guint32 options = 0; /* use default info if not supplied */ if(!info) { info = &definfo; } /* write structure size */ ipatch_file_buf_write_u32(riff->handle, IPATCH_DLS_WSMP_HEADER_SIZE); ipatch_file_buf_write_u16(riff->handle, info->root_note); ipatch_file_buf_write_u16(riff->handle, info->fine_tune); ipatch_file_buf_write_u32(riff->handle, info->gain); if(info->options & IPATCH_DLS2_SAMPLE_NO_TRUNCATION) { options |= IPATCH_DLS_WSMP_NO_TRUNCATION; } if(info->options & IPATCH_DLS2_SAMPLE_NO_COMPRESSION) { options |= IPATCH_DLS_WSMP_NO_COMPRESSION; } ipatch_file_buf_write_u32(riff->handle, options); /* FIXME: Preserve header expansion data? */ loop_type = info->options & IPATCH_DLS2_SAMPLE_LOOP_MASK; ipatch_file_buf_write_u32(riff->handle, (loop_type != IPATCH_SAMPLE_LOOP_NONE) ? 1 : 0); /* loop count */ if(loop_type != IPATCH_SAMPLE_LOOP_NONE) { /* write loop structure size */ ipatch_file_buf_write_u32(riff->handle, IPATCH_DLS_WSMP_LOOP_SIZE); if(loop_type == IPATCH_SAMPLE_LOOP_RELEASE) { saveloop_type = IPATCH_DLS_WSMP_LOOP_RELEASE; } else { saveloop_type = IPATCH_DLS_WSMP_LOOP_FORWARD; /* default */ } ipatch_file_buf_write_u32(riff->handle, saveloop_type); ipatch_file_buf_write_u32(riff->handle, info->loop_start); ipatch_file_buf_write_u32(riff->handle, info->loop_end - info->loop_start); } if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } static gboolean dls_write_sample_format(IpatchDLSWriter *writer, IpatchDLS2Sample *sample, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); int format; int frame_size; int channels; g_return_val_if_fail(sample->sample_data != NULL, FALSE); /* get format from primary store */ format = ipatch_sample_get_format(IPATCH_SAMPLE(sample->sample_data)); frame_size = ipatch_sample_format_size(format); channels = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format); g_return_val_if_fail(channels == 1 || channels == 2, FALSE); /* for right now we just support PCM FIXME - What about floating point?? */ ipatch_file_buf_write_u16(riff->handle, IPATCH_RIFF_WAVE_FMT_PCM); ipatch_file_buf_write_u16(riff->handle, channels); /* write channels */ ipatch_file_buf_write_u32(riff->handle, sample->rate); /* write dwAvgBytesPerSec and wBlockAlign fields */ ipatch_file_buf_write_u32(riff->handle, frame_size * sample->rate); ipatch_file_buf_write_u16(riff->handle, frame_size); /* bit width of audio */ ipatch_file_buf_write_u16(riff->handle, ipatch_sample_format_width(format) * 8); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } /* write the pool table header and reserve entries for the total number of samples, the entries are fixed up later after the wave pool has been written */ static gboolean dls_reserve_pool_table(IpatchDLSWriter *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); guint count; /* write pool table header size */ if(!ipatch_file_write_u32(riff->handle, IPATCH_DLS_PTBL_HEADER_SIZE, err)) { return (FALSE); } /* FIXME: Preserve header expansion? */ count = writer->sample_count; if(!ipatch_file_write_u32(riff->handle, count, err)) /* write sample cue count */ { return (FALSE); } /* get position of pool table cues for later fixup */ writer->ptbl_pos = ipatch_file_get_position(riff->handle); /* reserve the pool cues (one for each sample) */ if(!ipatch_file_seek(riff->handle, writer->sample_count * IPATCH_DLS_POOLCUE_SIZE, G_SEEK_CUR, err)) { return (FALSE); } return (TRUE); } static gboolean dls_fixup_pool_table(IpatchDLSWriter *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); guint32 retpos; retpos = ipatch_file_get_position(riff->handle); /* seek back to pool cue table */ if(!ipatch_file_seek(riff->handle, writer->ptbl_pos, G_SEEK_SET, err)) { return (FALSE); } /* do endian swap on cue offsets if needed */ if(IPATCH_RIFF_NEED_SWAP(riff)) { guint32 *cuep, *stop; cuep = writer->sample_ofstbl; stop = cuep + writer->sample_count; for(; cuep < stop; cuep++) { *cuep = GUINT32_SWAP_LE_BE(*cuep); } } /* write the table */ if(!ipatch_file_write(riff->handle, writer->sample_ofstbl, writer->sample_count * 4, err)) { return (FALSE); } /* seek back to original position */ if(!ipatch_file_seek(riff->handle, retpos, G_SEEK_SET, err)) { return (FALSE); } return (TRUE); } static gboolean dls_write_wave_pool(IpatchDLSWriter *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchDLS2Sample *sample; IpatchSampleHandle sample_handle; IpatchIter iter; gpointer buf; guint samsize, read_size, ofs; guint index; guint32 start; int dest_fmt, fmt_size; /* start position of wave pool chunk */ start = ipatch_file_get_position(riff->handle); if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->dls), &iter, IPATCH_TYPE_DLS2_SAMPLE)) { return (FALSE); } sample = ipatch_dls2_sample_first(&iter); for(index = 0; sample; index++, sample = ipatch_dls2_sample_next(&iter)) { /* store offset to WAVE list chunk for later pool table fixup */ writer->sample_ofstbl[index] = ipatch_file_get_position(riff->handle) - start; /* - Wave list chunk */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_WAVE, err)) { goto err; } /* write DLSID if its set */ if(sample->dlid) if(!dls_write_dlid(writer, sample->dlid, err)) { return (FALSE); } /* - Sample format chunk */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_FMT, err)) { goto err; } if(!dls_write_sample_format(writer, sample, err)) { goto err; } if(!ipatch_riff_close_chunk(riff, -1, err)) { goto err; } /* */ /* - Sample text info */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_DLS_FOURCC_INFO, err)) { goto err; } if(!writer->is_gig) { if(!dls_write_info(writer, sample->info, err)) { goto err; } } else if(!gig_write_name_info(writer, sample->info, err)) { goto err; } if(!ipatch_riff_close_chunk(riff, -1, err)) { goto err; } /* */ if(!writer->is_gig && sample->sample_info) { /* - Wave sample info chunk */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_WSMP, err)) { goto err; } if(!dls_write_sample_info(writer, sample->sample_info, err)) { goto err; } if(!ipatch_riff_close_chunk(riff, -1, err)) { goto err; } /* */ } /* - Sample data */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_DATA, err)) { goto err; } /* store position to sample data for possible ipatch_dls_writer_create_stores() call */ writer->sample_postbl[index] = ipatch_file_get_position(riff->handle); samsize = ipatch_sample_get_size(IPATCH_SAMPLE(sample->sample_data), NULL); dest_fmt = ipatch_sample_get_format(IPATCH_SAMPLE(sample->sample_data)); dest_fmt &= IPATCH_SAMPLE_WIDTH_MASK | IPATCH_SAMPLE_CHANNEL_MASK; dest_fmt |= IPATCH_SAMPLE_LENDIAN; /* write 8 or 16 bit data FIXME - Support other bit widths? */ if(IPATCH_SAMPLE_FORMAT_GET_WIDTH(dest_fmt) == IPATCH_SAMPLE_8BIT) { dest_fmt |= IPATCH_SAMPLE_UNSIGNED; } else if(IPATCH_SAMPLE_FORMAT_GET_WIDTH(dest_fmt) >= IPATCH_SAMPLE_16BIT) { dest_fmt |= IPATCH_SAMPLE_SIGNED; dest_fmt &= ~IPATCH_SAMPLE_WIDTH_MASK; dest_fmt |= IPATCH_SAMPLE_16BIT; } /* frame size of dest format */ fmt_size = ipatch_sample_format_size(dest_fmt); /* ++ Open sample data handle (set transform manually) */ if(!ipatch_sample_handle_open(IPATCH_SAMPLE(sample->sample_data), &sample_handle, 'r', dest_fmt, IPATCH_SAMPLE_UNITY_CHANNEL_MAP, err)) { goto err; } read_size = ipatch_sample_handle_get_max_frames(&sample_handle); ofs = 0; while(ofs < samsize) { if(samsize - ofs < read_size) /* check for last partial fragment */ { read_size = samsize - ofs; } /* read and transform (if necessary) audio data from sample store */ if(!(buf = ipatch_sample_handle_read(&sample_handle, ofs, read_size, NULL, err))) { ipatch_sample_handle_close(&sample_handle); /* -- close sample handle */ goto err; } /* write sample data to DLS file */ if(!ipatch_file_write(riff->handle, buf, read_size * fmt_size, err)) { ipatch_sample_handle_close(&sample_handle); /* -- close sample handle */ goto err; } ofs += read_size; } ipatch_sample_handle_close(&sample_handle); /* -- close sample handle */ if(!ipatch_riff_close_chunk(riff, -1, err)) { goto err; } /* */ if(writer->is_gig) { /* - GigaSampler sample info chunk */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_GIG_FOURCC_SMPL, err)) { goto err; } if(!gig_write_sample_info(writer, sample->sample_info, sample->rate, err)) { goto err; } if(!ipatch_riff_close_chunk(riff, -1, err)) { goto err; } /* */ /* <3GIX> - GigaSampler sample group number */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_GIG_FOURCC_3GIX, err)) { goto err; } if(!ipatch_file_write_u32(riff->handle, IPATCH_GIG_SAMPLE(sample)->group_number, err)) { goto err; } if(!ipatch_riff_close_chunk(riff, -1, err)) { goto err; } /* */ } if(!ipatch_riff_close_chunk(riff, -1, err)) { goto err; } /* */ } return (TRUE); err: return (FALSE); } static gboolean dls_write_dlid(IpatchDLSWriter *writer, guint8 *dlid, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); /* FIXME - Generate a new DLSID if needed */ /* - DLSID chunk */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_DLID, err)) { return (FALSE); } if(!ipatch_file_write(riff->handle, dlid, 16, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ return (TRUE); } static gboolean gig_write_sub_regions(IpatchDLSWriter *writer, IpatchGigRegion *region, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchGigSubRegion *sub_region; int i; for(i = 0; i < region->sub_region_count; i++) { /* <3EWL> - GigaSampler sub region list chunk */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_GIG_FOURCC_3EWL, err)) { return (FALSE); } sub_region = region->sub_regions[i]; /* - GigaSampler sample info chunk */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_DLS_FOURCC_WSMP, err)) { return (FALSE); } if(!dls_write_sample_info(writer, sub_region->sample_info, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ /* <3EWA> - GigaSampler effects chunk */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_GIG_FOURCC_3EWA, err)) { return (FALSE); } ipatch_gig_store_effects(riff->handle, &sub_region->effects); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ } return (TRUE); } static gboolean gig_write_dimension_names(IpatchDLSWriter *writer, IpatchGigRegion *region, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchGigDimension *dim; int i; for(i = 0; i < region->dimension_count; i++) { dim = region->dimensions[i]; if(dim->name && *dim->name) { if(!ipatch_riff_write_chunk(riff, IPATCH_RIFF_CHUNK_SUB, IPATCH_FOURCC('n', 'a', 'm', '0' + i), err)) { return (FALSE); } if(!ipatch_file_write(riff->handle, dim->name, strlen(dim->name) + 1, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } } } return (TRUE); } /* for now we just use the standard DLS sample info */ static gboolean gig_write_sample_info(IpatchDLSWriter *writer, IpatchDLS2SampleInfo *info, int rate, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); ipatch_file_buf_write_u32(riff->handle, 0); /* manufacturer */ ipatch_file_buf_write_u32(riff->handle, 0); /* product */ /* period of 1 sample in nanoseconds (1 / rate * 1,000,000,000) */ ipatch_file_buf_write_u32(riff->handle, 1000000000 / rate); ipatch_file_buf_write_u32(riff->handle, info->root_note); /* SMPL chunk stores finetune as an unsigned 32bit fraction of a semitone, so 0x80000000 is 1/2 of a MIDI note. DLS on the other hand uses a 16 bit signed relative pitch value (methinks this is rel_pitch = cents / 100 * 32768, but not sure!! FIXME!!) */ ipatch_file_buf_write_u32(riff->handle, 0); ipatch_file_buf_write_u32(riff->handle, 0); /* SMPTE format */ ipatch_file_buf_write_u32(riff->handle, 0); /* SMPTE offset */ /* loop count */ ipatch_file_buf_write_u32(riff->handle, info->options == IPATCH_SAMPLE_LOOP_NONE ? 0 : 1); ipatch_file_buf_write_u32(riff->handle, 0); /* extra data size */ /* loop fields are always written, even when no loop */ ipatch_file_buf_write_u32(riff->handle, 0); /* loop ID */ /* FIXME - Is there a release WSMP loop type or other types? */ ipatch_file_buf_write_u32(riff->handle, 0); /* loop type - Normal */ ipatch_file_buf_write_u32(riff->handle, info->loop_start); /* loop start */ ipatch_file_buf_write_u32(riff->handle, info->loop_end); /* loop end */ ipatch_file_buf_write_u32(riff->handle, 0); /* loop sample fraction */ ipatch_file_buf_write_u32(riff->handle, 0); /* times to loop (0=inf) */ if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } static gboolean gig_write_dimension_info(IpatchDLSWriter *writer, IpatchGigRegion *region, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchGigDimension *dimension; int count; guint sample_index; int i; count = region->sub_region_count; ipatch_file_buf_write_u32(riff->handle, count); /* count of sub regions */ for(i = 0; i < region->dimension_count; i++) { dimension = region->dimensions[i]; /* type of dimension */ ipatch_file_buf_write_u8(riff->handle, dimension->type); /* split bit count */ ipatch_file_buf_write_u8(riff->handle, dimension->split_count); /* FIXME - 6 bytes of unknown data */ ipatch_file_buf_zero(riff->handle, 6); } /* zero out remaining unused dimensions */ i = (5 - region->dimension_count) * 8; if(i) { ipatch_file_buf_zero(riff->handle, i); } /* write sub region sample indexes */ for(i = 0; i < region->sub_region_count; i++) { sample_index = GPOINTER_TO_UINT (g_hash_table_lookup(writer->sample_hash, region->sub_regions[i]->sample)); g_return_val_if_fail(sample_index != 0, FALSE); ipatch_file_buf_write_u32(riff->handle, sample_index - 1); } /* fill remaining sample cue indexes with 0xFFFFFFFF */ i = (32 - region->sub_region_count) * 4; if(i) { ipatch_file_buf_memset(riff->handle, 0xFF, i); } if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } /* write GigaSampler 3gri chunk (sample group names) */ static gboolean gig_write_group_names(IpatchDLSWriter *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchGig *gig = IPATCH_GIG(writer->dls); char name[IPATCH_GIG_3GNM_SIZE]; GSList *p; /* <3gri> - GigaSampler 3gri list chunk */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_GIG_FOURCC_3GRI, err)) { return (FALSE); } /* <3gnl> - GigaSampler 3dnl list chunk */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_GIG_FOURCC_3GNL, err)) { return (FALSE); } for(p = gig->group_names; p; p = p->next) { /* <3gnm> - GigaSampler sample info chunk */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_GIG_FOURCC_3GNM, err)) { return (FALSE); } /* write the sample group name */ strncpy(name, (char *)(p->data), IPATCH_GIG_3GNM_SIZE); if(!ipatch_file_write(riff->handle, name, IPATCH_GIG_3GNM_SIZE, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ return (TRUE); } libinstpatch-1.1.6/libinstpatch/IpatchDLSWriter.h000066400000000000000000000057411400263525300220170ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_DLS_WRITER_H__ #define __IPATCH_DLS_WRITER_H__ #include #include #include #include #include typedef struct _IpatchDLSWriter IpatchDLSWriter; /* private structure */ typedef struct _IpatchDLSWriterClass IpatchDLSWriterClass; #define IPATCH_TYPE_DLS_WRITER (ipatch_dls_writer_get_type ()) #define IPATCH_DLS_WRITER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_DLS_WRITER, \ IpatchDLSWriter)) #define IPATCH_DLS_WRITER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_DLS_WRITER, \ IpatchDLSWriterClass)) #define IPATCH_IS_DLS_WRITER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_DLS_WRITER)) #define IPATCH_IS_DLS_WRITER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_DLS_WRITER)) /* DLS writer object */ struct _IpatchDLSWriter { IpatchRiff parent_instance; /* derived from IpatchRiff */ IpatchDLS2 *orig_dls; /* original DLS object */ IpatchDLS2 *dls; /* duplicated DLS object to save */ gboolean is_gig; /* set to TRUE if saving a GigaSampler file */ GHashTable *sample_hash; /* IpatchDLS2Sample -> sample_array index + 1 */ guint32 *sample_ofstbl; /* sample index -> file offset table */ guint32 *sample_postbl; /* sample index -> sample file position */ guint sample_count; /* count of samples */ guint32 ptbl_pos; /* pool table position in file - for later fixup */ IpatchList *store_list; /* list of stores, only set if ipatch_dls_writer_create_stores() was called */ }; /* DLS writer class */ struct _IpatchDLSWriterClass { IpatchRiffClass parent_class; }; GType ipatch_dls_writer_get_type(void); IpatchDLSWriter *ipatch_dls_writer_new(IpatchFileHandle *handle, IpatchDLS2 *dls); void ipatch_dls_writer_set_patch(IpatchDLSWriter *writer, IpatchDLS2 *dls); void ipatch_dls_writer_set_file_handle(IpatchDLSWriter *writer, IpatchFileHandle *handle); gboolean ipatch_dls_writer_save(IpatchDLSWriter *writer, GError **err); IpatchList *ipatch_dls_writer_create_stores(IpatchDLSWriter *writer); #endif libinstpatch-1.1.6/libinstpatch/IpatchFile.c000066400000000000000000001672041400263525300210550ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchFile * @short_description: File abstraction object * @see_also: * @stability: Stable * * Provides an abstraction of file data sources and file type identification. */ #include #include #include /* for stat and fstat */ #include #include #include #include #include #include "IpatchFile.h" #include "ipatch_priv.h" #include "util.h" // Count of new files in file pool hash before garbage collection cleanup is run #define IPATCH_FILE_POOL_CREATE_COUNT_CLEANUP 100 enum { PROP_0, PROP_FILE_NAME }; #define IPATCH_FILE_FREE_IOFUNCS(file) \ (ipatch_item_get_flags(file) & IPATCH_FILE_FLAG_FREE_IOFUNCS) static void ipatch_file_class_init(IpatchFileClass *klass); static void ipatch_file_init(IpatchFile *file); static void ipatch_file_finalize(GObject *gobject); static void ipatch_file_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_file_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gboolean ipatch_file_real_set_name(IpatchFile *file, const char *file_name); static GType ipatch_file_real_identify(IpatchFile *file, gboolean byext, GError **err); static GType *type_all_children(GType type, GArray *pass_array); static gint sort_type_by_identify_order(gconstpointer a, gconstpointer b); static gboolean ipatch_file_null_open_method(IpatchFileHandle *handle, const char *mode, GError **err); static GIOStatus ipatch_file_null_read_method(IpatchFileHandle *handle, gpointer buf, guint size, guint *bytes_read, GError **err); static GIOStatus ipatch_file_null_write_method(IpatchFileHandle *handle, gconstpointer buf, guint size, GError **err); static GIOStatus ipatch_file_null_seek_method(IpatchFileHandle *handle, int offset, GSeekType type, GError **err); /* default methods GIOChannel based methods */ static IpatchFileIOFuncs default_iofuncs = { ipatch_file_default_open_method, ipatch_file_default_close_method, ipatch_file_default_read_method, ipatch_file_default_write_method, ipatch_file_default_seek_method, ipatch_file_default_getfd_method, ipatch_file_default_get_size_method }; /* null methods (/dev/null like iofuncs) */ static IpatchFileIOFuncs null_iofuncs = { ipatch_file_null_open_method, NULL, /* close method */ ipatch_file_null_read_method, ipatch_file_null_write_method, ipatch_file_null_seek_method, NULL, /* get fd method */ NULL /* get_size method */ }; G_DEFINE_TYPE(IpatchFile, ipatch_file, IPATCH_TYPE_ITEM) /* Lock and hash for file pool */ G_LOCK_DEFINE_STATIC(ipatch_file_pool); static GHashTable *ipatch_file_pool = NULL; // Hash of fileNames -> GWeakRef(IpatchFile) /* Initialise static hash and create IpatchFile class */ /* ----- Initialization/deinitialization of ipatch_file pool ---------------*/ /* Initialise static hash */ void _ipatch_file_init(void) { /* create file pool has table */ ipatch_file_pool = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, ipatch_util_weakref_destroy); } /* Free static hash */ void _ipatch_file_deinit(void) { g_hash_table_destroy(ipatch_file_pool); } /* ----- IpatchFileHandle object functions ---------------------------------*/ static IpatchFileHandle * ipatch_file_handle_duplicate(IpatchFileHandle *handle) { IpatchFileHandle *newhandle; g_return_val_if_fail(handle != NULL, NULL); g_return_val_if_fail(IPATCH_IS_FILE(handle->file), NULL); newhandle = g_slice_new0(IpatchFileHandle); /* ++ alloc handle */ newhandle->file = g_object_ref(handle->file); /* ++ ref file */ return (newhandle); } static void ipatch_file_handle_free(IpatchFileHandle *handle) { g_return_if_fail(handle != NULL); g_return_if_fail(IPATCH_IS_FILE(handle->file)); g_object_unref(handle->file); /* -- unref file */ g_slice_free(IpatchFileHandle, handle); } /** * ipatch_file_handle_get_type: * * Get boxed type for #IpatchFileHandle * * Returns: Boxed type for file handle. * * Since: 1.1.0 */ GType ipatch_file_handle_get_type(void) { static GType type = 0; if(!type) type = g_boxed_type_register_static("IpatchFileHandle", (GBoxedCopyFunc)ipatch_file_handle_duplicate, (GBoxedFreeFunc)ipatch_file_handle_free); return (type); } static void ipatch_file_class_init(IpatchFileClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->finalize = ipatch_file_finalize; obj_class->get_property = ipatch_file_get_property; item_class->item_set_property = ipatch_file_set_property; klass->identify = NULL; g_object_class_install_property(obj_class, PROP_FILE_NAME, g_param_spec_string("file-name", "File Name", "File Name", NULL, G_PARAM_READWRITE)); } static void ipatch_file_init(IpatchFile *file) { file->iofuncs = &default_iofuncs; ipatch_item_clear_flags(file, IPATCH_FILE_FLAG_FREE_IOFUNCS); if(G_BYTE_ORDER != G_LITTLE_ENDIAN) { ipatch_item_set_flags(file, IPATCH_FILE_FLAG_SWAP); } file->ref_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, ipatch_util_weakref_destroy); } static void ipatch_file_finalize(GObject *gobject) { IpatchFile *file = IPATCH_FILE(gobject); IPATCH_ITEM_WLOCK(file); /* No handles will be open, since they hold refs on the file */ /* free iofuncs structure if needed */ if(file->iofuncs && ipatch_item_get_flags(file) & IPATCH_FILE_FLAG_FREE_IOFUNCS) { g_slice_free(IpatchFileIOFuncs, file->iofuncs); file->iofuncs = NULL; } g_free(file->file_name); file->file_name = NULL; if(file->iochan) { g_io_channel_unref(file->iochan); } g_hash_table_destroy(file->ref_hash); IPATCH_ITEM_WUNLOCK(file); if(G_OBJECT_CLASS(ipatch_file_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_file_parent_class)->finalize(gobject); } } static void ipatch_file_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchFile *file = IPATCH_FILE(object); switch(property_id) { case PROP_FILE_NAME: ipatch_file_real_set_name(file, g_value_get_string(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_file_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchFile *file = IPATCH_FILE(object); switch(property_id) { case PROP_FILE_NAME: g_value_take_string(value, ipatch_file_get_name(file)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /** * ipatch_file_new: * * Create a new file object * * Returns: The new file object */ IpatchFile * ipatch_file_new(void) { return (IPATCH_FILE(g_object_new(IPATCH_TYPE_FILE, NULL))); } /** * ipatch_file_pool_new: * @file_name: File name (converted to an absolute file name if it isn't already) * @created: (out) (optional): Location to store %TRUE if file object was * newly created, %FALSE if not (%NULL to ignore) * * Lookup existing file object from file pool by file name or create a new one if not open. * * Returns: File object with the assigned @file_name and an added reference which the caller owns * * Since: 1.1.0 */ IpatchFile * ipatch_file_pool_new(const char *file_name, gboolean *created) { IpatchFile *file, *lookup_file = NULL; char *abs_filename; GWeakRef *weakref, *lookup; static int createCount = 0; // Counter for garbage collection (destroyed file objects) if(created) { *created = FALSE; // Initialize in case of bail out.. } g_return_val_if_fail(file_name != NULL, NULL); file = ipatch_file_new(); // ++ ref weakref = g_slice_new(GWeakRef); // ++ allocate weak reference g_weak_ref_init(weakref, file); abs_filename = ipatch_util_abs_filename(file_name); // ++ allocate absolute filename G_LOCK(ipatch_file_pool); lookup = g_hash_table_lookup(ipatch_file_pool, abs_filename); if(lookup) { lookup_file = g_weak_ref_get(lookup); // ++ ref object if(!lookup_file) { g_weak_ref_set(lookup, file); // !! Re-use weak reference (it was NULL) } } else { g_hash_table_insert(ipatch_file_pool, abs_filename, weakref); // !! hash takes over filename and weakref } if(!lookup_file) { if(++createCount >= IPATCH_FILE_POOL_CREATE_COUNT_CLEANUP) // Garbage collection for destroyed file objects { GHashTableIter iter; gpointer key, value; IpatchFile *value_file; createCount = 0; g_hash_table_iter_init(&iter, ipatch_file_pool); while(g_hash_table_iter_next(&iter, &key, &value)) { value_file = g_weak_ref_get((GWeakRef *)value); // ++ ref if(value_file) { g_object_unref(value_file); // -- unref file value } else { g_hash_table_iter_remove(&iter); // Weak reference empty (file object destroyed) - remove } } } } G_UNLOCK(ipatch_file_pool); if(lookup_file) { g_free(abs_filename); // -- free absolute filename g_weak_ref_clear(weakref); // -- clear weak reference g_slice_free(GWeakRef, weakref); // -- free weak reference g_object_unref(file); // -- unref return (lookup_file); // !! caller takes over ref } if(created) { *created = TRUE; } if(lookup) { g_free(abs_filename); // -- free absolute filename g_weak_ref_clear(weakref); // -- clear weak reference g_slice_free(GWeakRef, weakref); // -- free weak reference } return (file); // !! caller takes over ref } /** * ipatch_file_pool_lookup: * @file_name: File name to lookup existing file object for * * Lookup an existing file object in the file pool, by file name. Does not * create a new object, if not found, like ipatch_file_pool_new() does. * * Returns: (transfer full): Matching file object with a reference that the caller owns * or %NULL if not found * * Since: 1.1.0 */ IpatchFile * ipatch_file_pool_lookup(const char *file_name) { IpatchFile *lookup_file = NULL; char *abs_filename; GWeakRef *lookup; g_return_val_if_fail(file_name != NULL, NULL); abs_filename = ipatch_util_abs_filename(file_name); // ++ allocate absolute filename G_LOCK(ipatch_file_pool); lookup = g_hash_table_lookup(ipatch_file_pool, abs_filename); if(lookup) { lookup_file = g_weak_ref_get(lookup); // ++ ref object } G_UNLOCK(ipatch_file_pool); g_free(abs_filename); // -- free absolute filename return (lookup_file); // !! caller takes over ref } /** * ipatch_file_ref_from_object: * @file: File object to add a reference to (g_object_ref() called) * @object: Object which is referencing the @file * * Reference a file object from another object and keep track of the * external reference (using a #GWeakRef). References can be obtained with * ipatch_file_get_refs(). Use ipatch_file_unref_from_object() to remove * the reference, although the registration will be removed regardless at some * point if @object gets destroyed and ipatch_file_get_refs() or * ipatch_file_get_refs_by_type() is called. * * Since: 1.1.0 */ void ipatch_file_ref_from_object(IpatchFile *file, GObject *object) { GWeakRef *weakref; g_return_if_fail(IPATCH_IS_FILE(file)); g_return_if_fail(G_IS_OBJECT(object)); weakref = g_slice_new(GWeakRef); // ++ allocate weak reference g_weak_ref_init(weakref, object); // ++ initialize the weak reference with object IPATCH_ITEM_WLOCK(file); g_hash_table_insert(file->ref_hash, object, weakref); // !! list takes over weak reference IPATCH_ITEM_WUNLOCK(file); g_object_ref(file); // ++ ref file object for object } /** * ipatch_file_unref_from_object: * @file: File object to remove a reference from (g_object_unref() called) * @object: Object which is unreferencing the @file * * Remove a reference previously registered with ipatch_file_ref_from_object(). * This will get done eventually if @object gets destroyed and ipatch_file_get_refs() * or ipatch_file_get_refs_by_type() is called, however. * * Since: 1.1.0 */ void ipatch_file_unref_from_object(IpatchFile *file, GObject *object) { g_return_if_fail(IPATCH_IS_FILE(file)); g_return_if_fail(object != NULL); // We only need the pointer value really IPATCH_ITEM_WLOCK(file); g_hash_table_remove(file->ref_hash, object); IPATCH_ITEM_WUNLOCK(file); g_object_unref(file); // -- ref file object for object } /** * ipatch_file_test_ref_object: * @file: File object to test reference to * @object: Object to test for reference to @file * * Check if a given @object is referencing @file. Must have been * referenced with ipatch_file_ref_from_object(). * * Returns: %TRUE if @object references @file, %FALSE otherwise * * Since: 1.1.0 */ gboolean ipatch_file_test_ref_object(IpatchFile *file, GObject *object) { gboolean retval; g_return_val_if_fail(IPATCH_IS_FILE(file), FALSE); g_return_val_if_fail(object != NULL, FALSE); // We only need the pointer value really IPATCH_ITEM_WLOCK(file); retval = g_hash_table_lookup(file->ref_hash, object) != NULL; IPATCH_ITEM_WUNLOCK(file); return (retval); } /** * ipatch_file_get_refs: * @file: File object to get external references of * * Get list of objects referencing a file object. * NOTE: A side effect of calling this function is that any references from * destroyed objects are removed (if ipatch_file_unref_from_object() was not used). * * Returns: (transfer full): New object list which caller owns a reference to, * unreference when finished using it. * * Since: 1.1.0 */ IpatchList * ipatch_file_get_refs(IpatchFile *file) { return (ipatch_file_get_refs_by_type(file, G_TYPE_NONE)); } /** * ipatch_file_get_refs_by_type: * @file: File object to get external references of * @type: Object type to match (or a descendant thereof) or #G_TYPE_NONE * to match any type * * Like ipatch_file_get_refs() but only returns objects matching a given type * or a descendant thereof. * * Returns: (transfer full): New object list which caller owns a reference to, * unreference when finished using it. * * Since: 1.1.0 */ IpatchList * ipatch_file_get_refs_by_type(IpatchFile *file, GType type) { GHashTableIter iter; gpointer key; GObject *refobj; GWeakRef *weakref; IpatchList *list; g_return_val_if_fail(IPATCH_IS_FILE(file), NULL); if(type == G_TYPE_OBJECT) { type = G_TYPE_NONE; // G_TYPE_OBJECT is equivalent to G_TYPE_NONE (i.e., all objects) } g_return_val_if_fail(type == G_TYPE_NONE || g_type_is_a(type, G_TYPE_OBJECT), NULL); list = ipatch_list_new(); // ++ ref object list IPATCH_ITEM_WLOCK(file); g_hash_table_iter_init(&iter, file->ref_hash); while(g_hash_table_iter_next(&iter, &key, (gpointer *)&weakref)) { refobj = g_weak_ref_get(weakref); // ++ ref object if(refobj) // Object still alive? { // type not specified or object matches type? if(type == G_TYPE_NONE || g_type_is_a(G_OBJECT_TYPE(refobj), type)) { list->items = g_list_prepend(list->items, refobj); // Prepend object to list } else { g_object_unref(refobj); // -- unref object (did not match type criteria) } } else { g_hash_table_iter_remove(&iter); // Object destroyed - remove from hash } } IPATCH_ITEM_WUNLOCK(file); return (list); // !! caller takes over list reference } /** * ipatch_file_set_name: * @file: File object to assign file name to * @file_name: File name or %NULL to unset the file name * * Sets the file name of a file object. Assigning the file name of an #IpatchFile * object is optional, since a file descriptor could be assigned instead, * but some subsystems depend on it. */ void ipatch_file_set_name(IpatchFile *file, const char *file_name) { if(ipatch_file_real_set_name(file, file_name)) { g_object_notify(G_OBJECT(file), "file-name"); } } static gboolean ipatch_file_real_set_name(IpatchFile *file, const char *file_name) { char *new_filename, *old_filename; g_return_val_if_fail(IPATCH_IS_FILE(file), FALSE); new_filename = g_strdup(file_name); // ++ alloc file name for file object IPATCH_ITEM_WLOCK(file); old_filename = file->file_name; file->file_name = new_filename; // !! takes over allocation IPATCH_ITEM_WUNLOCK(file); g_free(old_filename); // -- free old file name return (TRUE); } /** * ipatch_file_get_name: * @file: File object to get file name from * * Gets the assigned file name from a file object. * * Returns: The file name of the file object or %NULL if not set. String * should be freed when finished with it. */ char * ipatch_file_get_name(IpatchFile *file) { char *file_name = NULL; g_return_val_if_fail(IPATCH_IS_FILE(file), NULL); IPATCH_ITEM_RLOCK(file); if(file->file_name) { file_name = g_strdup(file->file_name); } IPATCH_ITEM_RUNLOCK(file); return (file_name); } /** * ipatch_file_rename: * @file: File object to rename * @new_name: New file name (can be a full path to move the file) * @err: Location to store error info or %NULL to ignore * * Physically rename the file referenced by a @file object. The given file * object must have a file name assigned and no file descriptor or I/O channel. * On Windows, the file must also not have any open handles. If a file with * @new_name already exists, it will be replaced and should not be referenced by * any file object. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set) * * Since: 1.1.0 */ gboolean ipatch_file_rename(IpatchFile *file, const char *new_name, GError **err) { char *dup_newname, *old_filename; IpatchFile *new_name_file; g_return_val_if_fail(IPATCH_IS_FILE(file), FALSE); g_return_val_if_fail(new_name != NULL, FALSE); g_return_val_if_fail(!err || !*err, FALSE); // Check if new file name is already referenced by a file object new_name_file = ipatch_file_pool_lookup(new_name); // ++ ref file object if(new_name_file) { g_object_unref(new_name_file); // -- unref file object (only need pointer value) g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_BUSY, "New file name '%s' is already claimed", new_name); return (FALSE); } #ifdef G_OS_WIN32 if(g_file_test(new_name, G_FILE_TEST_EXISTS)) { g_unlink(new_name); // Just blindly unlink the file } #endif dup_newname = g_strdup(new_name); // ++ allocate for use by file object IPATCH_ITEM_WLOCK(file); if(log_if_fail(file->iochan == NULL)) { goto error; } if(log_if_fail(file->file_name != NULL)) { goto error; } // Don't even try renaming the file on Windows if it is open, should be fine on Unix for most purposes #ifdef G_OS_WIN32 if(file->open_count > 0) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_BUSY, "File '%s' has open handles", file->file_name); goto error; } #endif if(g_rename(file->file_name, dup_newname) != 0) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, "I/O error renaming file '%s' to '%s': %s", file->file_name, dup_newname, g_strerror(errno)); goto error; } old_filename = file->file_name; file->file_name = dup_newname; // !! takes over allocation IPATCH_ITEM_WUNLOCK(file); g_free(old_filename); // -- free old file name return (TRUE); error: IPATCH_ITEM_WUNLOCK(file); g_free(dup_newname); // -- free duplicate copy of original new_name return (FALSE); } /** * ipatch_file_unlink: * @file: File object to rename * @err: Location to store error info or %NULL to ignore * * Physically delete the file referenced by a @file object. The given file * object must have a file name assigned and no file descriptor or I/O channel. * On Windows, the file must also not have any open handles. * The file object will remain alive, but the underlying file will be unlinked. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set) * * Since: 1.1.0 */ gboolean ipatch_file_unlink(IpatchFile *file, GError **err) { g_return_val_if_fail(IPATCH_IS_FILE(file), FALSE); g_return_val_if_fail(!err || !*err, FALSE); IPATCH_ITEM_WLOCK(file); if(log_if_fail(file->iochan == NULL)) { goto error; } if(log_if_fail(file->file_name != NULL)) { goto error; } // Don't even try deleting the file on Windows if it is open, should be fine on Unix for most purposes #ifdef G_OS_WIN32 if(file->open_count > 0) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_BUSY, "File '%s' has open handles", file->file_name); goto error; } #endif if(g_unlink(file->file_name) != 0) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, "I/O error unlinking file '%s': %s", file->file_name, g_strerror(errno)); goto error; } IPATCH_ITEM_WUNLOCK(file); return (TRUE); error: IPATCH_ITEM_WUNLOCK(file); return (FALSE); } /** * ipatch_file_replace: * @newfile: New file to replace the @oldfile with (must have an assigned #IpatchFile::file-name property) * @oldfile: The old file to replace (must have an assigned #IpatchFile::file-name property) * @err: Location to store error info or %NULL to ignore * * Replace one file object with another. After successful execution of this function * @oldfile will have an unset file name, @newfile will be assigned what was the oldfile name, * and the file data of the old file on the filesystem will have been replaced by new file. * * NOTE: On Windows both files must not have any open file handles. * * NOTE: In the event an error occurs, recovery will be attempted, but may also fail, resulting in * loss of @oldfile data. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set) * * Since: 1.1.0 */ gboolean ipatch_file_replace(IpatchFile *newfile, IpatchFile *oldfile, GError **err) { char *filename, *free_filename; g_return_val_if_fail(IPATCH_IS_FILE(newfile), FALSE); g_return_val_if_fail(IPATCH_IS_FILE(oldfile), FALSE); g_return_val_if_fail(!err || !*err, FALSE); // Sanity check of files, prior to doing any funny business IPATCH_ITEM_RLOCK(oldfile); if(log_if_fail(oldfile->iochan == NULL) || log_if_fail(oldfile->file_name != NULL)) { IPATCH_ITEM_RUNLOCK(oldfile); return (FALSE); } // Don't even try replacing the oldfile on Windows if open, should be fine on Unix for most purposes #ifdef G_OS_WIN32 if(oldfile->open_count > 0) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_BUSY, "Old file '%s' has open handles", oldfile->file_name); IPATCH_ITEM_RUNLOCK(oldfile); return (FALSE); } #endif IPATCH_ITEM_RUNLOCK(oldfile); IPATCH_ITEM_RLOCK(newfile); if(log_if_fail(newfile->iochan == NULL) || log_if_fail(newfile->file_name != NULL)) { IPATCH_ITEM_RUNLOCK(newfile); return (FALSE); } // Don't even try renaming the newfile on Windows if open, should be fine on Unix for most purposes #ifdef G_OS_WIN32 if(newfile->open_count > 0) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_BUSY, "New file '%s' has open handles", newfile->file_name); IPATCH_ITEM_RUNLOCK(newfile); return (FALSE); } #endif IPATCH_ITEM_RUNLOCK(newfile); // Steal filename from oldfile and delete file (on Windows) IPATCH_ITEM_WLOCK(oldfile); // filename must be valid before calling de g_unlink(). filename = oldfile->file_name; // ++ filename takes over allocation #ifdef G_OS_WIN32 // Just blindly unlink the file g_unlink(filename); #endif oldfile->file_name = NULL; IPATCH_ITEM_WUNLOCK(oldfile); // Rename newfile to oldfile name and assign the file name to it IPATCH_ITEM_WLOCK(newfile); if(g_rename(newfile->file_name, filename) != 0) { // WARNING - On windows, if rename fails, oldfile is lost.. Unlikely to happen though. g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, "I/O error renaming file '%s' to '%s': %s", newfile->file_name, filename, g_strerror(errno)); IPATCH_ITEM_WUNLOCK(newfile); #ifdef G_OS_WIN32 g_free(filename); // -- free the oldfile filename (on Windows oldfile has been deleted) #else // Restore oldfile file name on Unix IPATCH_ITEM_WLOCK(oldfile); free_filename = oldfile->file_name; // ++ take over file name which may have been assigned by another thread (highly unlikely..) oldfile->file_name = filename; IPATCH_ITEM_WUNLOCK(oldfile); g_free(free_filename); // -- free possibly newly assigned file name #endif return (FALSE); } free_filename = newfile->file_name; // ++ free_filename takes over allocation newfile->file_name = filename; // !! newfile takes over allocation IPATCH_ITEM_WUNLOCK(newfile); g_free(free_filename); // -- free the previous newfile file name return (TRUE); } /** * ipatch_file_open: * @file: File object to open from a file name. * @file_name: (nullable): Name of file to open or %NULL to use the file object's assigned file name (in which case it should not be %NULL). * @mode: File open mode ("r" for read or "w" for write) * @err: Error information * * Opens a handle to a file object. If a I/O channel or file descriptor is * already assigned (with ipatch_file_assign_fd() or * ipatch_file_assign_io_channel()) then it is used instead of opening a file * using @file_name or the already assigned #IpatchFile:file-name property. * * Returns: New file handle or %NULL (in which case @err may be set). The * returned file handle is not multi-thread safe, but the file can be opened * multiple times for this purpose. */ IpatchFileHandle * ipatch_file_open(IpatchFile *file, const char *file_name, const char *mode, GError **err) { IpatchFileHandle *handle; GIOChannel *iochan = NULL; char *old_file_name = NULL; char *dup_file_name; int retval = FALSE; g_return_val_if_fail(IPATCH_IS_FILE(file), NULL); g_return_val_if_fail(file->iofuncs != NULL, NULL); dup_file_name = g_strdup(file_name); /* ++ dup file name */ handle = g_slice_new0(IpatchFileHandle); /* ++ alloc handle */ handle->file = file; IPATCH_ITEM_WLOCK(file); if(log_if_fail(file->iofuncs->open != NULL)) { IPATCH_ITEM_WUNLOCK(file); g_slice_free(IpatchFileHandle, handle); /* -- free handle */ g_free(dup_file_name); /* -- free dup file name */ return (NULL); } if(dup_file_name) /* set file name if supplied */ { old_file_name = file->file_name; file->file_name = dup_file_name; /* !! takes over allocation of dup file name */ } if(file->iochan) { iochan = g_io_channel_ref(file->iochan); /* ++ ref io channel */ handle->iochan = iochan; } retval = file->iofuncs->open(handle, mode, err); if(retval) { file->open_count++; } IPATCH_ITEM_WUNLOCK(file); g_free(old_file_name); /* -- free old file name */ if(!retval) { g_slice_free(IpatchFileHandle, handle); /* -- free handle */ if(iochan) { g_io_channel_unref(iochan); /* -- unref iochan */ } return (NULL); } g_object_ref(file); /* ++ ref file for handle */ handle->buf = g_byte_array_new(); return (handle); /* !! caller takes over handle */ } /** * ipatch_file_assign_fd: * @file: File object * @fd: File descriptor to assign to file object, or -1 to clear it * @close_on_finalize: %TRUE if the descriptor should be closed when @file is * finalized, %FALSE to leave it open * * Assigns a file descriptor to a file, which gets used for calls to * ipatch_file_open(). Note that this means multiple opens will use the same * file descriptor and will therefore conflict, so it should only be used in the * case where the file object is used by a single exclusive handle. */ void ipatch_file_assign_fd(IpatchFile *file, int fd, gboolean close_on_finalize) { GIOChannel *iochan; g_return_if_fail(IPATCH_IS_FILE(file)); if(fd == -1) { ipatch_file_assign_io_channel(file, NULL); return; } iochan = g_io_channel_unix_new(fd); /* ++ ref new io channel */ g_io_channel_set_close_on_unref(iochan, close_on_finalize); g_io_channel_set_encoding(iochan, NULL, NULL); ipatch_file_assign_io_channel(file, iochan); g_io_channel_unref(iochan); /* -- unref creator's ref */ } /** * ipatch_file_assign_io_channel: * @file: File object * @iochan: IO channel to assign to @file or %NULL to clear it * * Assigns an I/O channel to a file, which gets used for calls to * ipatch_file_open(). Note that this means multiple opens will use the same * file descriptor and will therefore conflict, so it should only be used in the * case where the file object is used by a single exclusive handle. */ void ipatch_file_assign_io_channel(IpatchFile *file, GIOChannel *iochan) { GIOChannel *old_iochan; g_return_if_fail(IPATCH_IS_FILE(file)); if(iochan) { g_io_channel_ref(iochan); /* ++ ref for file */ } IPATCH_ITEM_WLOCK(file); old_iochan = file->iochan; file->iochan = iochan; IPATCH_ITEM_WUNLOCK(file); if(old_iochan) { g_io_channel_unref(old_iochan); } } /** * ipatch_file_get_io_channel: * @handle: File handle * * Get the glib IO channel object from a file handle. The caller owns a * reference to the returned io channel, and it should be unreferenced with * g_io_channel_unref() when finished with it. * * Returns: GIOChannel assigned to the @handle or %NULL if none (some * derived #IpatchFile types might not use io channels). Remember to unref it * with g_io_channel_unref() when finished. */ GIOChannel * ipatch_file_get_io_channel(IpatchFileHandle *handle) { GIOChannel *iochan; g_return_val_if_fail(handle != NULL, NULL); if((iochan = handle->iochan)) { g_io_channel_ref(iochan); } return (iochan); } /** * ipatch_file_get_fd: * @handle: File handle * * Get the unix file descriptor associated with a file handle. Not all file * handles have a real OS file descriptor. * * Returns: File descriptor or -1 if not open or failed to get descriptor. */ int ipatch_file_get_fd(IpatchFileHandle *handle) { int fd = -1; g_return_val_if_fail(handle != NULL, -1); g_return_val_if_fail(IPATCH_IS_FILE(handle->file), -1); if(handle->file->iofuncs && handle->file->iofuncs->getfd) { fd = handle->file->iofuncs->getfd(handle); } return (fd); } /** * ipatch_file_close: * @handle: File handle * * Close a file handle and free it. */ void ipatch_file_close(IpatchFileHandle *handle) { g_return_if_fail(handle != NULL); g_return_if_fail(IPATCH_IS_FILE(handle->file)); IPATCH_ITEM_WLOCK(handle->file); if(handle->file->iofuncs && handle->file->iofuncs->close) { handle->file->iofuncs->close(handle); } handle->file->open_count--; IPATCH_ITEM_WUNLOCK(handle->file); g_object_unref(handle->file); if(handle->buf) { g_byte_array_free(handle->buf, TRUE); } if(handle->iochan) { g_io_channel_unref(handle->iochan); } g_slice_free(IpatchFileHandle, handle); } /** * ipatch_file_get_position: * @handle: File handle * * Gets the current position in a file handle. Note that this * might not be the actual position in the file if the file handle was * attached to an already open file or if ipatch_file_update_position() is * used to set virtual positions. * * Returns: Position in file handle. */ guint ipatch_file_get_position(IpatchFileHandle *handle) { g_return_val_if_fail(handle != NULL, 0); return (handle->position); } /** * ipatch_file_update_position: * @handle: File handle * @offset: Offset to add to the position counter (can be negative) * * Adds an offset value to the position counter in a file handle. This can * be used if one is operating directly on the underlying file descriptor (i.e., * not using the #IpatchFile functions) or to add virtual space to the counter. * Adding virtual space is useful when a system uses the position counter to * write data (such as the RIFF parser) to act as a place holder for data that * isn't actually written (sample data for example). */ void ipatch_file_update_position(IpatchFileHandle *handle, guint offset) { g_return_if_fail(handle != NULL); handle->position += offset; } /** * ipatch_file_read: * @handle: File handle * @buf: (out) (array length=size) (element-type guint8): Buffer to read data into * @size: Amount of data to read, in bytes. * @err: A location to return an error of type GIOChannelError or %NULL. * * Reads data from a file handle. An end of file encountered while * trying to read the specified @size of data is treated as an error. * If this is undesirable use ipatch_file_read_eof() instead. * * Returns: %TRUE on success (the requested @size of data was read), %FALSE * otherwise */ gboolean ipatch_file_read(IpatchFileHandle *handle, gpointer buf, guint size, GError **err) { GIOStatus status; /* we let ipatch_file_read_eof do checks for us */ status = ipatch_file_read_eof(handle, buf, size, NULL, err); if(status == G_IO_STATUS_EOF) { if(err && !*err) g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNEXPECTED_EOF, _("Unexpected end of file")); return (FALSE); } return (status == G_IO_STATUS_NORMAL); } /** * ipatch_file_read_eof: * @handle: File handle * @buf: (out) (array length=size) (element-type guint8): Buffer to read data into * @size: Amount of data to read, in bytes. * @bytes_read: (out) (optional): Pointer to store number of bytes actually read or %NULL. * @err: A location to return an error of type GIOChannelError or %NULL. * * Reads data from a file handle. This function does not treat end of file * as an error and will return #G_IO_STATUS_EOF with the number of bytes * actually read in @bytes_read. Use ipatch_file_read() for convenience to * ensure actual number of requested bytes is read. * * Returns: The status of the operation */ GIOStatus ipatch_file_read_eof(IpatchFileHandle *handle, gpointer buf, guint size, guint *bytes_read, GError **err) { GIOStatus status; guint _bytes_read = 0; if(bytes_read) { *bytes_read = 0; } g_return_val_if_fail(handle != NULL, G_IO_STATUS_ERROR); g_return_val_if_fail(IPATCH_IS_FILE(handle->file), G_IO_STATUS_ERROR); g_return_val_if_fail(handle->file->iofuncs != NULL, G_IO_STATUS_ERROR); g_return_val_if_fail(handle->file->iofuncs->read != NULL, G_IO_STATUS_ERROR); g_return_val_if_fail(buf != NULL, G_IO_STATUS_ERROR); g_return_val_if_fail(size > 0, FALSE); g_return_val_if_fail(!err || !*err, G_IO_STATUS_ERROR); status = handle->file->iofuncs->read(handle, buf, size, &_bytes_read, err); if(bytes_read) { *bytes_read = _bytes_read; } handle->position += _bytes_read; return (status); } /** * _ipatch_file_read_no_pos_update: (skip) * * Used internally by IpatchFileBuf. Like ipatch_file_read() but does not * update handle->position, since buffered functions do this themselves. */ gboolean _ipatch_file_read_no_pos_update(IpatchFileHandle *handle, gpointer buf, guint size, GError **err) { guint _bytes_read = 0; return (handle->file->iofuncs->read(handle, buf, size, &_bytes_read, err) == G_IO_STATUS_NORMAL); } /** * ipatch_file_write: * @handle: File handle * @buf: (array length=size) (element-type guint8): Buffer of data to write * @size: Amount of data to write, in bytes. * @err: A location to return an error of type GIOChannelError or %NULL. * * Writes data to a file object. * * Returns: TRUE on success, FALSE otherwise */ gboolean ipatch_file_write(IpatchFileHandle *handle, gconstpointer buf, guint size, GError **err) { GIOStatus status; g_return_val_if_fail(handle != NULL, G_IO_STATUS_ERROR); g_return_val_if_fail(IPATCH_IS_FILE(handle->file), G_IO_STATUS_ERROR); g_return_val_if_fail(handle->file->iofuncs != NULL, G_IO_STATUS_ERROR); g_return_val_if_fail(handle->file->iofuncs->write != NULL, G_IO_STATUS_ERROR); g_return_val_if_fail(buf != NULL, G_IO_STATUS_ERROR); g_return_val_if_fail(size > 0, FALSE); g_return_val_if_fail(!err || !*err, G_IO_STATUS_ERROR); status = handle->file->iofuncs->write(handle, buf, size, err); if(status == G_IO_STATUS_NORMAL) { handle->position += size; } return (status == G_IO_STATUS_NORMAL); } /** * _ipatch_file_write_no_pos_update: (skip) * * Used internally by IpatchFileBuf. Like ipatch_file_write() but does not * update handle->position, since buffered functions do this themselves. */ gboolean _ipatch_file_write_no_pos_update(IpatchFileHandle *handle, gconstpointer buf, guint size, GError **err) { return (handle->file->iofuncs->write(handle, buf, size, err) == G_IO_STATUS_NORMAL); } /** * ipatch_file_seek: * @handle: File handle * @offset: Offset in bytes to seek from the position specified by @type * @type: Position in file to seek from (see g_io_channel_seek_position * for more details, only #G_SEEK_CUR and #G_SEEK_SET allowed). * @err: A location to return error info or %NULL. * * Seek in a file handle. An end of file condition is treated as an error, use * ipatch_file_seek_eof() if this is undesirable. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean ipatch_file_seek(IpatchFileHandle *handle, int offset, GSeekType type, GError **err) { GIOStatus status; /* we let ipatch_file_seek_eof do checks for us */ status = ipatch_file_seek_eof(handle, offset, type, err); if(status == G_IO_STATUS_EOF) { if(err && !*err) g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNEXPECTED_EOF, _("Unexpected end of file")); return (FALSE); } return (status == G_IO_STATUS_NORMAL); } /** * ipatch_file_seek_eof: * @handle: File handle * @offset: Offset in bytes to seek from the position specified by @type * @type: Position in file to seek from (see g_io_channel_seek_position * for more details, only G_SEEK_CUR and G_SEEK_SET allowed). * @err: A location to return error info or %NULL. * * Seek in a file object. Does not treat end of file as an error, use * ipatch_file_seek() for convenience if this is desirable. * * Returns: The status of the operation */ GIOStatus ipatch_file_seek_eof(IpatchFileHandle *handle, int offset, GSeekType type, GError **err) { GIOStatus status; g_return_val_if_fail(handle != NULL, G_IO_STATUS_ERROR); g_return_val_if_fail(IPATCH_IS_FILE(handle->file), G_IO_STATUS_ERROR); g_return_val_if_fail(handle->file->iofuncs != NULL, G_IO_STATUS_ERROR); g_return_val_if_fail(handle->file->iofuncs->seek != NULL, G_IO_STATUS_ERROR); g_return_val_if_fail(type == G_SEEK_CUR || type == G_SEEK_SET, G_IO_STATUS_ERROR); g_return_val_if_fail(!err || !*err, G_IO_STATUS_ERROR); status = handle->file->iofuncs->seek(handle, offset, type, err); if(status == G_IO_STATUS_NORMAL) { if(type == G_SEEK_SET) { handle->position = offset; } else { handle->position += offset; } } return (status); } /** * ipatch_file_get_size: * @file: File object to get size of * @err: Location to store error information * * Get the size of a file object. * * Returns: File size or -1 on error or if operation unsupported by this file * object (in which case @err may be set) */ int ipatch_file_get_size(IpatchFile *file, GError **err) { int size = -1; g_return_val_if_fail(IPATCH_IS_FILE(file), -1); g_return_val_if_fail(file->iofuncs != NULL, -1); g_return_val_if_fail(!err || !*err, -1); if(file->iofuncs->get_size) /* has get_size IO function? */ { size = file->iofuncs->get_size(file, err); } return (size); } /** * ipatch_file_identify: * @file: File object to identify type of * @err: Location to store error information * * Attempts to identify the type of a file using the "identify" method of * registered types derived from #IpatchFile. The #IpatchFile:file-name property * should already be assigned. * * Returns: The first #IpatchFile derived type that had an "identify" method * which returned %TRUE, or 0 if unknown file type or error, in which * case error information will be stored in @err provided its not %NULL. */ GType ipatch_file_identify(IpatchFile *file, GError **err) { return (ipatch_file_real_identify(file, FALSE, err)); } /** * ipatch_file_identify_name: * @filename: Name of file to identify type of * @err: Location to store error information * * Like ipatch_file_identify() but uses a file name for convenience. * * Returns: The first #IpatchFile derived type that had an "identify" method * which returned %TRUE, or 0 if unknown file type or error, in which * case error information will be stored in @err provided its not %NULL. * * Since: 1.1.0 */ GType ipatch_file_identify_name(const char *filename, GError **err) { IpatchFile *file; GType type; g_return_val_if_fail(filename != NULL, 0); file = ipatch_file_new(); // ++ ref file ipatch_file_set_name(file, filename); type = ipatch_file_identify(file, err); g_object_unref(file); // -- unref file return (type); } /** * ipatch_file_identify_by_ext: * @file: File object to identify type of * * Attempts to identify the type of a file using the "identify" method of * registered types derived from #IpatchFile. The #IpatchFile:file-name property * should already be assigned. Like ipatch_file_identify() but identifies a * file by its file name extension only. * * Returns: The first #IpatchFile derived type that had an "identify" method * which returned %TRUE, or 0 if unknown file type or error, in which * case error information will be stored in @err provided its not %NULL. */ GType ipatch_file_identify_by_ext(IpatchFile *file) { return (ipatch_file_real_identify(file, TRUE, NULL)); } static GType ipatch_file_real_identify(IpatchFile *file, gboolean byext, GError **err) { IpatchFileHandle *handle = NULL; IpatchFileClass *file_class; GType *children, *p; GType found = 0; GError *local_err = NULL; g_return_val_if_fail(IPATCH_IS_FILE(file), 0); g_return_val_if_fail(file->file_name != NULL, 0); if(!byext) { /* Open a handle to the file */ handle = ipatch_file_open(file, NULL, "r", err); if(!handle) { return (0); } } children = type_all_children(IPATCH_TYPE_FILE, NULL); for(p = children; p && *p; p++) { file_class = g_type_class_ref(*p); if(!file_class) { continue; } if(file_class->identify) { if(!file_class->identify(file, handle, &local_err)) { if(local_err) { g_propagate_error(err, local_err); g_type_class_unref(file_class); if(handle) { ipatch_file_close(handle); } return (0); } } else { found = *p; } if(handle) { ipatch_file_seek(handle, 0, G_SEEK_SET, NULL); } } g_type_class_unref(file_class); if(found != 0) { break; } } if(handle) { ipatch_file_close(handle); } return (found); } static GType * type_all_children(GType type, GArray *pass_array) { static GType *types = NULL; GArray *array = pass_array; GType *children; int i; if(types) { return (types); } if(!array) { array = g_array_new(TRUE, FALSE, sizeof(GType)); /* Zero terminated */ } children = g_type_children(type, NULL); if(children) { for(i = 0; children[i]; i++) { type_all_children(children[i], array); g_array_append_val(array, children[i]); } g_free(children); } if(!pass_array) /* last iteration? */ { types = (array->len > 0) ? (GType *)(array->data) : NULL; g_array_sort(array, sort_type_by_identify_order); g_array_free(array, FALSE); return (types); } else { return (NULL); } } /* Sort IpatchFile types by their identify_order class field (largest value first order) */ static gint sort_type_by_identify_order(gconstpointer a, gconstpointer b) { const GType *typea = a, *typeb = b; IpatchFileClass *klassa, *klassb; gint retval; klassa = g_type_class_ref(*typea); /* ++ ref class */ klassb = g_type_class_ref(*typeb); /* ++ ref class */ retval = klassb->identify_order - klassa->identify_order; g_type_class_unref(klassa); /* -- unref class */ g_type_class_unref(klassb); /* -- unref class */ return (retval); } /** * ipatch_file_identify_open: * @file_name: File name to identify and open * @err: Location to store error of type GIOChannelError * * A convenience function which calls ipatch_file_identify() to determine the * file type of @file_name. If the type is identified a new file object, of the * identified type, is created and the file is opened with * ipatch_file_open() in read mode. * * Returns: The new opened handle of the identified type or %NULL if unable to * identify. Caller should free the handle with ipatch_file_close() when done * using it, at which point the parent #IpatchFile will be destroyed if no other * reference is held. */ IpatchFileHandle * ipatch_file_identify_open(const char *file_name, GError **err) { IpatchFileHandle *handle; IpatchFile *file; GType file_type; g_return_val_if_fail(file_name != NULL, NULL); g_return_val_if_fail(!err || !*err, NULL); file = ipatch_file_new(); /* ++ ref new file */ ipatch_file_set_name(file, file_name); file_type = ipatch_file_identify(file, err); g_object_unref(file); /* -- unref file */ if(file_type == 0) { return (NULL); } file = g_object_new(file_type, NULL); /* ++ ref file */ handle = ipatch_file_open(file, file_name, "r", err); /* ++ alloc handle */ g_object_unref(file); /* -- unref file (handle holds a ref) */ return (handle); /* !! caller takes over handle */ } /** * ipatch_file_identify_new: * @file_name: File name to identify and create file object for * @err: Location to store error of type GIOChannelError * * A convenience function which calls ipatch_file_identify() to determine the * file type of @file_name. If the type is identified a new file object, of the * identified type, is created and returned. * * Returns: The new file of the identified type or %NULL if unable to * identify. Caller owns a reference and should remove it when done using it. * * Since: 1.1.0 */ IpatchFile * ipatch_file_identify_new(const char *file_name, GError **err) { IpatchFileHandle *handle; IpatchFile *file; handle = ipatch_file_identify_open(file_name, err); if(!handle) { return (NULL); } file = handle->file; g_object_ref(file); // ++ ref for caller ipatch_file_close(handle); return (file); // !! caller takes over reference } /** * ipatch_file_set_little_endian: * @file: File object * * Sets the file object to little endian mode (the default mode). * If the system is big endian, byte swapping will be enabled * (see IPATCH_FILE_SWAPxx macros). The endian mode affects buffered * read and write functions that operate on multi-byte integers. */ void ipatch_file_set_little_endian(IpatchFile *file) { g_return_if_fail(IPATCH_IS_FILE(file)); IPATCH_ITEM_WLOCK(file); ipatch_item_clear_flags(file, IPATCH_FILE_FLAG_BIG_ENDIAN); if(G_BYTE_ORDER != G_LITTLE_ENDIAN) { ipatch_item_set_flags(file, IPATCH_FILE_FLAG_SWAP); } IPATCH_ITEM_WUNLOCK(file); } /** * ipatch_file_set_big_endian: * @file: File object * * Sets the file object to big endian mode (the default is little endian). * If the system is little endian, byte swapping will be enabled * (see IPATCH_FILE_SWAPxx macros). The endian mode affects buffered * read and write functions that operate on multi-byte integers. */ void ipatch_file_set_big_endian(IpatchFile *file) { g_return_if_fail(IPATCH_IS_FILE(file)); IPATCH_ITEM_WLOCK(file); ipatch_item_set_flags(file, IPATCH_FILE_FLAG_BIG_ENDIAN); if(G_BYTE_ORDER != G_BIG_ENDIAN) { ipatch_item_set_flags(file, IPATCH_FILE_FLAG_SWAP); } IPATCH_ITEM_WUNLOCK(file); } /** * ipatch_file_set_iofuncs_static: * @file: File object * @funcs: Static IO functions structure or %NULL to set to defaults * * Sets the input/output functions of a file object using a statically * allocated (guaranteed to exist for lifetime of @file) functions structure. * Setting these functions allows one to write custom data sources or hook * into other file functions. */ void ipatch_file_set_iofuncs_static(IpatchFile *file, IpatchFileIOFuncs *funcs) { g_return_if_fail(IPATCH_IS_FILE(file)); IPATCH_ITEM_WLOCK(file); if(IPATCH_FILE_FREE_IOFUNCS(file)) { g_slice_free(IpatchFileIOFuncs, file->iofuncs); } file->iofuncs = funcs ? funcs : &default_iofuncs; ipatch_item_clear_flags(file, IPATCH_FILE_FLAG_FREE_IOFUNCS); IPATCH_ITEM_WUNLOCK(file); } /** * ipatch_file_set_iofuncs: * @file: File object * @funcs: IO functions structure or %NULL to set to defaults * * Sets the input/output functions of a file object. The @funcs * structure is duplicated so as not to use the original, see * ipatch_file_set_iofuncs_static() for using a static structure. * Setting these functions allows one to write custom data sources or * hook into other file functions. */ void ipatch_file_set_iofuncs(IpatchFile *file, const IpatchFileIOFuncs *funcs) { IpatchFileIOFuncs *dupfuncs = NULL; g_return_if_fail(IPATCH_IS_FILE(file)); if(funcs) /* duplicate functions structure */ { dupfuncs = g_slice_new(IpatchFileIOFuncs); *dupfuncs = *funcs; } IPATCH_ITEM_WLOCK(file); if(IPATCH_FILE_FREE_IOFUNCS(file)) { g_slice_free(IpatchFileIOFuncs, file->iofuncs); } file->iofuncs = dupfuncs ? dupfuncs : &default_iofuncs; if(dupfuncs) { ipatch_item_set_flags(file, IPATCH_FILE_FLAG_FREE_IOFUNCS); } else { ipatch_item_clear_flags(file, IPATCH_FILE_FLAG_FREE_IOFUNCS); } IPATCH_ITEM_WUNLOCK(file); } /** * ipatch_file_get_iofuncs: * @file: File object * @out_funcs: Location to store current IO functions to * * Get the current IO functions from a file object. The function pointers * are stored in a user supplied structure pointed to by @out_funcs. */ void ipatch_file_get_iofuncs(IpatchFile *file, IpatchFileIOFuncs *out_funcs) { g_return_if_fail(IPATCH_IS_FILE(file)); g_return_if_fail(out_funcs != NULL); IPATCH_ITEM_RLOCK(file); *out_funcs = *file->iofuncs; IPATCH_ITEM_RUNLOCK(file); } /** * ipatch_file_set_iofuncs_null: * @file: File object * * Sets the I/O functions of a file object to /dev/null like methods. * Reading from the file will return 0s, writing/seeking will do nothing. */ void ipatch_file_set_iofuncs_null(IpatchFile *file) { g_return_if_fail(IPATCH_IS_FILE(file)); ipatch_file_set_iofuncs_static(file, &null_iofuncs); } /** * ipatch_file_default_open_method: (skip) * @handle: File handle * @mode: File open mode * @err: Error info * * Default "open" method for #IpatchFileIOFuncs. Useful when overriding only * some I/O functions. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean ipatch_file_default_open_method(IpatchFileHandle *handle, const char *mode, GError **err) { if(handle->iochan) /* io channel has been explicitly set? */ { g_io_channel_set_encoding(handle->iochan, NULL, NULL); return (TRUE); } g_return_val_if_fail(mode != NULL, FALSE); g_return_val_if_fail(handle->file->file_name != NULL, FALSE); handle->iochan = g_io_channel_new_file(handle->file->file_name, mode, err); if(handle->iochan) { g_io_channel_set_encoding(handle->iochan, NULL, NULL); return (TRUE); } else { return (FALSE); } } /** * ipatch_file_default_close_method: (skip) * @handle: File handle * * Default "close" method for #IpatchFileIOFuncs. Useful when overriding only * some I/O functions. */ void ipatch_file_default_close_method(IpatchFileHandle *handle) { g_return_if_fail(handle->iochan != NULL); g_io_channel_shutdown(handle->iochan, TRUE, NULL); g_io_channel_unref(handle->iochan); handle->iochan = NULL; } /** * ipatch_file_default_read_method: (skip) * @handle: File handle * @buf: Buffer to store data to * @size: Size of data * @bytes_read: Number of bytes actually read * @err: Error info * * Default "read" method for #IpatchFileIOFuncs. Useful when overriding only * some I/O functions. * * Returns: The status of the operation. */ GIOStatus ipatch_file_default_read_method(IpatchFileHandle *handle, gpointer buf, guint size, guint *bytes_read, GError **err) { gsize _bytes_read = 0; GIOStatus status; g_return_val_if_fail(handle->iochan != NULL, G_IO_STATUS_ERROR); status = g_io_channel_read_chars(handle->iochan, buf, size, &_bytes_read, err); *bytes_read = _bytes_read; return (status); } /** * ipatch_file_default_write_method: (skip) * @handle: File handle * @buf: Buffer to read data from * @size: Size of data * @err: Error info * * Default "write" method for #IpatchFileIOFuncs. Useful when overriding only * some I/O functions. * * Returns: The status of the operation. */ GIOStatus ipatch_file_default_write_method(IpatchFileHandle *handle, gconstpointer buf, guint size, GError **err) { GIOStatus status; g_return_val_if_fail(handle->iochan != NULL, G_IO_STATUS_ERROR); status = g_io_channel_write_chars(handle->iochan, buf, size, NULL, err); return (status); } /** * ipatch_file_default_seek_method: (skip) * @handle: File handle * @offset: Offset (depends on seek @type) * @type: Seek type * @err: Error info * * Default "seek" method for #IpatchFileIOFuncs. Useful when overriding only * some I/O functions. * * Returns: The status of the operation. */ GIOStatus ipatch_file_default_seek_method(IpatchFileHandle *handle, int offset, GSeekType type, GError **err) { g_return_val_if_fail(handle->iochan != NULL, G_IO_STATUS_ERROR); return (g_io_channel_seek_position(handle->iochan, offset, type, err)); } /** * ipatch_file_default_getfd_method: (skip) * @handle: File handle * * Default "getfd" method for #IpatchFileIOFuncs. Useful when overriding only * some I/O functions. This method gets a unix file descriptor for the given * file object, it is an optional method. * * Returns: Unix file descriptor or -1 if no file descriptor or error. */ int ipatch_file_default_getfd_method(IpatchFileHandle *handle) { return (handle->iochan ? g_io_channel_unix_get_fd(handle->iochan) : -1); } /** * ipatch_file_default_get_size_method: (skip) * @file: File object * @err: Error info * * Default get file size method, which is optional. * * Returns: File size or -1 on error (in which case @err may be set). */ int ipatch_file_default_get_size_method(IpatchFile *file, GError **err) { GStatBuf info; if(file->file_name) { if(g_stat(file->file_name, &info) != 0) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, _("Error during call to stat(\"%s\"): %s"), file->file_name, g_strerror(errno)); return (-1); } return (info.st_size); } else { return (-1); } } /* --------------------------------------------------------------- * NULL file iofunc methods (like /dev/null) * --------------------------------------------------------------- */ static gboolean ipatch_file_null_open_method(IpatchFileHandle *handle, const char *mode, GError **err) { return (TRUE); } static GIOStatus ipatch_file_null_read_method(IpatchFileHandle *handle, gpointer buf, guint size, guint *bytes_read, GError **err) { memset(buf, 0, size); *bytes_read = size; return (G_IO_STATUS_NORMAL); } static GIOStatus ipatch_file_null_write_method(IpatchFileHandle *handle, gconstpointer buf, guint size, GError **err) { return (G_IO_STATUS_NORMAL); } static GIOStatus ipatch_file_null_seek_method(IpatchFileHandle *handle, int offset, GSeekType type, GError **err) { return (G_IO_STATUS_NORMAL); } libinstpatch-1.1.6/libinstpatch/IpatchFile.h000066400000000000000000000313711400263525300210550ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_FILE_H__ #define __IPATCH_FILE_H__ #include #include #include /* forward type declarations */ typedef struct _IpatchFile IpatchFile; typedef struct _IpatchFileClass IpatchFileClass; typedef struct _IpatchFileIOFuncs IpatchFileIOFuncs; typedef struct _IpatchFileHandle IpatchFileHandle; #include #define IPATCH_TYPE_FILE (ipatch_file_get_type ()) #define IPATCH_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_FILE, IpatchFile)) #define IPATCH_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_FILE, IpatchFileClass)) #define IPATCH_IS_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_FILE)) #define IPATCH_IS_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_FILE)) #define IPATCH_FILE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_FILE, IpatchFileClass)) /* IO function table for IpatchFile instances */ struct _IpatchFileIOFuncs { gboolean(*open)(IpatchFileHandle *handle, const char *mode, GError **err); void (*close)(IpatchFileHandle *handle); GIOStatus(*read)(IpatchFileHandle *handle, gpointer buf, guint size, guint *bytes_read, GError **err); GIOStatus(*write)(IpatchFileHandle *handle, gconstpointer buf, guint size, GError **err); GIOStatus(*seek)(IpatchFileHandle *handle, int offset, GSeekType type, GError **err); int (*getfd)(IpatchFileHandle *handle); /* optional get file descriptor method */ int (*get_size)(IpatchFile *file, GError **err); /* optional get size method */ }; /* File object */ struct _IpatchFile { IpatchItem parent_instance; /*< private >*/ IpatchFileIOFuncs *iofuncs; /* per instance I/O methods */ char *file_name; /* not always set */ GIOChannel *iochan; /* assigned directly with ipatch_file_set_(fd/iochan) */ GHashTable *ref_hash; /* registered objects referencing this file: objectPtr -> GWeakRef */ guint open_count; /* count of open file handles */ }; /* File object class */ struct _IpatchFileClass { IpatchItemClass parent_class; /* File identify method */ gboolean(*identify)(IpatchFile *file, IpatchFileHandle *handle, GError **err); int identify_order; /* Identify execution order (see #IpatchFileIdentifyOrder, 0 = default) */ }; /** * IpatchFileFlags: * @IPATCH_FILE_FLAG_SWAP: Swap multi-byte numbers? * @IPATCH_FILE_FLAG_BIG_ENDIAN: Big endian file? * @IPATCH_FILE_FLAG_FREE_IOFUNCS: Should ->iofuncs be freed? */ typedef enum { IPATCH_FILE_FLAG_SWAP = 1 << (IPATCH_ITEM_UNUSED_FLAG_SHIFT), IPATCH_FILE_FLAG_BIG_ENDIAN = 1 << (IPATCH_ITEM_UNUSED_FLAG_SHIFT + 1), IPATCH_FILE_FLAG_FREE_IOFUNCS = 1 << (IPATCH_ITEM_UNUSED_FLAG_SHIFT + 2) } IpatchFileFlags; /** * IpatchFileIdentifyOrder: * @IPATCH_FILE_IDENTIFY_ORDER_LAST: Execute last (toward the end of the list) * @IPATCH_FILE_IDENTIFY_ORDER_DEFAULT: Default execution order (no preference) * @IPATCH_FILE_IDENTIFY_ORDER_FIRST: Execute first (toward the start of the list) * * Some helpful constants for the identify_order #IpatchFileClass field. Note * that any value can be used and this enum just provides some helpful values. * This value determines in what order file identification methods are called. * Higher values are executed first. */ typedef enum { IPATCH_FILE_IDENTIFY_ORDER_LAST = -10, IPATCH_FILE_IDENTIFY_ORDER_DEFAULT = 0, IPATCH_FILE_IDENTIFY_ORDER_FIRST = 10 } IpatchFileIdentifyOrder; /** * IPATCH_FILE_UNUSED_FLAG_SHIFT: (skip) */ /* reserve 6 flags (3 for expansion) */ #define IPATCH_FILE_UNUSED_FLAG_SHIFT (IPATCH_ITEM_UNUSED_FLAG_SHIFT + 6) /* macro to check if multi-byte numbers in file require swapping */ #define IPATCH_FILE_NEED_SWAP(file) \ (ipatch_item_get_flags(file) & IPATCH_FILE_FLAG_SWAP) /* macro to check if file is big endian */ #define IPATCH_FILE_BIG_ENDIAN(file) \ (ipatch_item_get_flags(file) & IPATCH_FILE_FLAG_BIG_ENDIAN) /* runtime byte swapping macros for convenient loading of cross endian files, takes a pointer to the value to swap (like in a buffer) */ #define IPATCH_FILE_SWAP16(file, p16) \ (IPATCH_FILE_NEED_SWAP (file) ? \ GUINT16_SWAP_LE_BE (*(guint16 *)(p16)) : *(guint16 *)(p16)) #define IPATCH_FILE_SWAP32(file, p32) \ (IPATCH_FILE_NEED_SWAP (file) ? \ GUINT32_SWAP_LE_BE (*(guint32 *)(p32)) : *(guint32 *)(p32)) #define IPATCH_FILE_SWAP64(file, p64) \ (IPATCH_FILE_NEED_SWAP (file) ? \ GUINT64_SWAP_LE_BE (*(guint64 *)(p64)) : *(guint64 *)(p64)) #define IPATCH_IS_FILE_HANDLE(handle) \ ((handle) && IPATCH_IS_FILE ((handle)->file)) /** * IpatchFileHandle: * * #IpatchFile handle for opening a file and reading/writing from/to it. */ struct _IpatchFileHandle { IpatchFile *file; /* Parent file object */ guint position; /* Current file position */ GByteArray *buf; /* For buffered reads/writes */ guint buf_position; /* Current position in buffer */ GIOChannel *iochan; /* glib IO channel (default methods) */ gpointer data; /* iofuncs defined data */ }; GType ipatch_file_get_type(void); GType ipatch_file_handle_get_type(void); IpatchFile *ipatch_file_new(void); IpatchFile *ipatch_file_pool_new(const char *file_name, gboolean *created); IpatchFile *ipatch_file_pool_lookup(const char *file_name); void ipatch_file_ref_from_object(IpatchFile *file, GObject *object); void ipatch_file_unref_from_object(IpatchFile *file, GObject *object); gboolean ipatch_file_test_ref_object(IpatchFile *file, GObject *object); IpatchList *ipatch_file_get_refs(IpatchFile *file); IpatchList *ipatch_file_get_refs_by_type(IpatchFile *file, GType type); void ipatch_file_set_name(IpatchFile *file, const char *file_name); char *ipatch_file_get_name(IpatchFile *file); gboolean ipatch_file_rename(IpatchFile *file, const char *new_name, GError **err); gboolean ipatch_file_unlink(IpatchFile *file, GError **err); gboolean ipatch_file_replace(IpatchFile *newfile, IpatchFile *oldfile, GError **err); IpatchFileHandle *ipatch_file_open(IpatchFile *file, const char *file_name, const char *mode, GError **err); void ipatch_file_assign_fd(IpatchFile *file, int fd, gboolean close_on_finalize); void ipatch_file_assign_io_channel(IpatchFile *file, GIOChannel *iochan); GIOChannel *ipatch_file_get_io_channel(IpatchFileHandle *handle); int ipatch_file_get_fd(IpatchFileHandle *handle); void ipatch_file_close(IpatchFileHandle *handle); guint ipatch_file_get_position(IpatchFileHandle *handle); void ipatch_file_update_position(IpatchFileHandle *handle, guint offset); gboolean ipatch_file_read(IpatchFileHandle *handle, gpointer buf, guint size, GError **err); GIOStatus ipatch_file_read_eof(IpatchFileHandle *handle, gpointer buf, guint size, guint *bytes_read, GError **err); gboolean ipatch_file_write(IpatchFileHandle *handle, gconstpointer buf, guint size, GError **err); #define ipatch_file_skip(handle, offset, err) \ ipatch_file_seek (handle, offset, G_SEEK_CUR, err) gboolean ipatch_file_seek(IpatchFileHandle *handle, int offset, GSeekType type, GError **err); GIOStatus ipatch_file_seek_eof(IpatchFileHandle *handle, int offset, GSeekType type, GError **err); int ipatch_file_get_size(IpatchFile *file, GError **err); GType ipatch_file_identify(IpatchFile *file, GError **err); GType ipatch_file_identify_name(const char *filename, GError **err); GType ipatch_file_identify_by_ext(IpatchFile *file); IpatchFileHandle *ipatch_file_identify_open(const char *file_name, GError **err); IpatchFile *ipatch_file_identify_new(const char *file_name, GError **err); void ipatch_file_set_little_endian(IpatchFile *file); void ipatch_file_set_big_endian(IpatchFile *file); gboolean ipatch_file_read_u8(IpatchFileHandle *handle, guint8 *val, GError **err); gboolean ipatch_file_read_u16(IpatchFileHandle *handle, guint16 *val, GError **err); gboolean ipatch_file_read_u32(IpatchFileHandle *handle, guint32 *val, GError **err); gboolean ipatch_file_read_u64(IpatchFileHandle *handle, guint64 *val, GError **err); gboolean ipatch_file_read_s8(IpatchFileHandle *handle, gint8 *val, GError **err); gboolean ipatch_file_read_s16(IpatchFileHandle *handle, gint16 *val, GError **err); gboolean ipatch_file_read_s32(IpatchFileHandle *handle, gint32 *val, GError **err); gboolean ipatch_file_read_s64(IpatchFileHandle *handle, gint64 *val, GError **err); gboolean ipatch_file_write_u8(IpatchFileHandle *handle, guint8 val, GError **err); gboolean ipatch_file_write_u16(IpatchFileHandle *handle, guint16 val, GError **err); gboolean ipatch_file_write_u32(IpatchFileHandle *handle, guint32 val, GError **err); gboolean ipatch_file_write_u64(IpatchFileHandle *handle, guint64 val, GError **err); gboolean ipatch_file_write_s8(IpatchFileHandle *handle, gint8 val, GError **err); gboolean ipatch_file_write_s16(IpatchFileHandle *handle, gint16 val, GError **err); gboolean ipatch_file_write_s32(IpatchFileHandle *handle, gint32 val, GError **err); gboolean ipatch_file_write_s64(IpatchFileHandle *handle, gint64 val, GError **err); gboolean ipatch_file_buf_load(IpatchFileHandle *handle, guint size, GError **err); void ipatch_file_buf_read(IpatchFileHandle *handle, gpointer buf, guint size); void ipatch_file_buf_write(IpatchFileHandle *handle, gconstpointer buf, guint size); #define ipatch_file_buf_zero(filebuf, size) \ ipatch_file_buf_memset(filebuf, 0, size) void ipatch_file_buf_memset(IpatchFileHandle *handle, unsigned char c, guint size); void ipatch_file_buf_set_size(IpatchFileHandle *handle, guint size); gboolean ipatch_file_buf_commit(IpatchFileHandle *handle, GError **err); #define ipatch_file_buf_skip(filebuf, offset) \ ipatch_file_buf_seek (filebuf, offset, G_SEEK_CUR) void ipatch_file_buf_seek(IpatchFileHandle *handle, int offset, GSeekType type); guint8 ipatch_file_buf_read_u8(IpatchFileHandle *handle); guint16 ipatch_file_buf_read_u16(IpatchFileHandle *handle); guint32 ipatch_file_buf_read_u32(IpatchFileHandle *handle); guint64 ipatch_file_buf_read_u64(IpatchFileHandle *handle); gint8 ipatch_file_buf_read_s8(IpatchFileHandle *handle); gint16 ipatch_file_buf_read_s16(IpatchFileHandle *handle); gint32 ipatch_file_buf_read_s32(IpatchFileHandle *handle); gint64 ipatch_file_buf_read_s64(IpatchFileHandle *handle); void ipatch_file_buf_write_u8(IpatchFileHandle *handle, guint8 val); void ipatch_file_buf_write_u16(IpatchFileHandle *handle, guint16 val); void ipatch_file_buf_write_u32(IpatchFileHandle *handle, guint32 val); void ipatch_file_buf_write_u64(IpatchFileHandle *handle, guint64 val); void ipatch_file_buf_write_s8(IpatchFileHandle *handle, gint8 val); void ipatch_file_buf_write_s16(IpatchFileHandle *handle, gint16 val); void ipatch_file_buf_write_s32(IpatchFileHandle *handle, gint32 val); void ipatch_file_buf_write_s64(IpatchFileHandle *handle, gint64 val); void ipatch_file_set_iofuncs_static(IpatchFile *file, IpatchFileIOFuncs *funcs); void ipatch_file_set_iofuncs(IpatchFile *file, const IpatchFileIOFuncs *funcs); void ipatch_file_get_iofuncs(IpatchFile *file, IpatchFileIOFuncs *out_funcs); void ipatch_file_set_iofuncs_null(IpatchFile *file); gboolean ipatch_file_default_open_method(IpatchFileHandle *handle, const char *mode, GError **err); void ipatch_file_default_close_method(IpatchFileHandle *handle); GIOStatus ipatch_file_default_read_method(IpatchFileHandle *handle, gpointer buf, guint size, guint *bytes_read, GError **err); GIOStatus ipatch_file_default_write_method(IpatchFileHandle *handle, gconstpointer buf, guint size, GError **err); GIOStatus ipatch_file_default_seek_method(IpatchFileHandle *handle, int offset, GSeekType type, GError **err); int ipatch_file_default_getfd_method(IpatchFileHandle *handle); int ipatch_file_default_get_size_method(IpatchFile *file, GError **err); #endif libinstpatch-1.1.6/libinstpatch/IpatchFileBuf.c000066400000000000000000000644401400263525300215100ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /* * IpatchFile.c - File buffer and integer read/write funcs */ #include #include #include #include #include "IpatchFile.h" /* Defined in IpatchFile.c */ extern gboolean _ipatch_file_read_no_pos_update(IpatchFileHandle *handle, gpointer buf, guint size, GError **err); extern gboolean _ipatch_file_write_no_pos_update(IpatchFileHandle *handle, gconstpointer buf, guint size, GError **err); /** * ipatch_file_read_u8: * @handle: File handle * @val: Location to store value * @err: Location to store error info or %NULL * * Read an unsigned 8 bit integer from a file. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_read_u8(IpatchFileHandle *handle, guint8 *val, GError **err) { return (ipatch_file_read(handle, val, sizeof(guint8), err)); } /** * ipatch_file_read_u16: * @handle: File handle * @val: Location to store value * @err: Location to store error info or %NULL * * Read an unsigned 16 bit integer from a file and performs endian * byte swapping if necessary. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_read_u16(IpatchFileHandle *handle, guint16 *val, GError **err) { if(!ipatch_file_read(handle, val, sizeof(guint16), err)) { return (FALSE); } *val = IPATCH_FILE_SWAP16(handle->file, val); return (TRUE); } /** * ipatch_file_read_u32: * @handle: File handle * @val: Location to store value * @err: Location to store error info or %NULL * * Read an unsigned 32 bit integer from a file and performs endian * byte swapping if necessary. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_read_u32(IpatchFileHandle *handle, guint32 *val, GError **err) { if(!ipatch_file_read(handle, val, sizeof(guint32), err)) { return (FALSE); } *val = IPATCH_FILE_SWAP32(handle->file, val); return (TRUE); } /** * ipatch_file_read_u64: * @handle: File handle * @val: Location to store value * @err: Location to store error info or %NULL * * Read an unsigned 64 bit integer from a file and performs endian * byte swapping if necessary. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_read_u64(IpatchFileHandle *handle, guint64 *val, GError **err) { if(!ipatch_file_read(handle, val, sizeof(guint64), err)) { return (FALSE); } *val = IPATCH_FILE_SWAP64(handle->file, val); return (TRUE); } /** * ipatch_file_read_s8: * @handle: File handle * @val: Location to store value * @err: Location to store error info or %NULL * * Read a signed 8 bit integer from a file. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_read_s8(IpatchFileHandle *handle, gint8 *val, GError **err) { return (ipatch_file_read(handle, val, sizeof(gint8), err)); } /** * ipatch_file_read_s16: * @handle: File handle * @val: Location to store value * @err: Location to store error info or %NULL * * Read a signed 16 bit integer from a file and performs endian * byte swapping if necessary. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_read_s16(IpatchFileHandle *handle, gint16 *val, GError **err) { if(!ipatch_file_read(handle, val, sizeof(gint16), err)) { return (FALSE); } *val = IPATCH_FILE_SWAP16(handle->file, val); return (TRUE); } /** * ipatch_file_read_s32: * @handle: File handle * @val: Location to store value * @err: Location to store error info or %NULL * * Read a signed 32 bit integer from a file and performs endian * byte swapping if necessary. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_read_s32(IpatchFileHandle *handle, gint32 *val, GError **err) { if(!ipatch_file_read(handle, val, sizeof(gint32), err)) { return (FALSE); } *val = IPATCH_FILE_SWAP32(handle->file, val); return (TRUE); } /** * ipatch_file_read_s64: * @handle: File handle * @val: Location to store value * @err: Location to store error info or %NULL * * Read a signed 64 bit integer from a file and performs endian * byte swapping if necessary. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_read_s64(IpatchFileHandle *handle, gint64 *val, GError **err) { if(!ipatch_file_read(handle, val, sizeof(gint64), err)) { return (FALSE); } *val = IPATCH_FILE_SWAP64(handle->file, val); return (TRUE); } /** * ipatch_file_write_u8: * @handle: File handle * @val: Value to store * @err: Location to store error info or %NULL * * Write an unsigned 8 bit integer to a file. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_write_u8(IpatchFileHandle *handle, guint8 val, GError **err) { return (ipatch_file_write(handle, &val, sizeof(guint8), err)); } /** * ipatch_file_write_u16: * @handle: File handle * @val: Value to store * @err: Location to store error info or %NULL * * Write an unsigned 16 bit integer to a file and performs endian * byte swapping if necessary. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_write_u16(IpatchFileHandle *handle, guint16 val, GError **err) { g_return_val_if_fail(handle != NULL, FALSE); g_return_val_if_fail(IPATCH_IS_FILE(handle->file), FALSE); val = IPATCH_FILE_SWAP16(handle->file, &val); if(!ipatch_file_write(handle, &val, sizeof(guint16), err)) { return (FALSE); } return (TRUE); } /** * ipatch_file_write_u32: * @handle: File handle * @val: Value to store * @err: Location to store error info or %NULL * * Write an unsigned 32 bit integer to a file and performs endian * byte swapping if necessary. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_write_u32(IpatchFileHandle *handle, guint32 val, GError **err) { g_return_val_if_fail(handle != NULL, FALSE); g_return_val_if_fail(IPATCH_IS_FILE(handle->file), FALSE); val = IPATCH_FILE_SWAP32(handle->file, &val); if(!ipatch_file_write(handle, &val, sizeof(guint32), err)) { return (FALSE); } return (TRUE); } /** * ipatch_file_write_u64: * @handle: File handle * @val: Value to store * @err: Location to store error info or %NULL * * Write an unsigned 64 bit integer to a file and performs endian * byte swapping if necessary. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_write_u64(IpatchFileHandle *handle, guint64 val, GError **err) { g_return_val_if_fail(handle != NULL, FALSE); g_return_val_if_fail(IPATCH_IS_FILE(handle->file), FALSE); val = IPATCH_FILE_SWAP64(handle->file, &val); if(!ipatch_file_write(handle, &val, sizeof(guint64), err)) { return (FALSE); } return (TRUE); } /** * ipatch_file_write_s8: * @handle: File handle * @val: Value to store * @err: Location to store error info or %NULL * * Write a signed 8 bit integer to a file. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_write_s8(IpatchFileHandle *handle, gint8 val, GError **err) { return (ipatch_file_write(handle, &val, sizeof(gint8), err)); } /** * ipatch_file_write_s16: * @handle: File handle * @val: Value to store * @err: Location to store error info or %NULL * * Write a signed 16 bit integer to a file and performs endian * byte swapping if necessary. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_write_s16(IpatchFileHandle *handle, gint16 val, GError **err) { g_return_val_if_fail(handle != NULL, FALSE); g_return_val_if_fail(IPATCH_IS_FILE(handle->file), FALSE); val = IPATCH_FILE_SWAP16(handle->file, &val); if(!ipatch_file_write(handle, &val, sizeof(gint16), err)) { return (FALSE); } return (TRUE); } /** * ipatch_file_write_s32: * @handle: File handle * @val: Value to store * @err: Location to store error info or %NULL * * Write a signed 32 bit integer to a file and performs endian * byte swapping if necessary. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_write_s32(IpatchFileHandle *handle, gint32 val, GError **err) { g_return_val_if_fail(handle != NULL, FALSE); g_return_val_if_fail(IPATCH_IS_FILE(handle->file), FALSE); val = IPATCH_FILE_SWAP32(handle->file, &val); if(!ipatch_file_write(handle, &val, sizeof(gint32), err)) { return (FALSE); } return (TRUE); } /** * ipatch_file_write_s64: * @handle: File handle * @val: Value to store * @err: Location to store error info or %NULL * * Write a signed 64 bit integer to a file and performs endian * byte swapping if necessary. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_file_write_s64(IpatchFileHandle *handle, gint64 val, GError **err) { g_return_val_if_fail(handle != NULL, FALSE); g_return_val_if_fail(IPATCH_IS_FILE(handle->file), FALSE); val = IPATCH_FILE_SWAP64(handle->file, &val); if(!ipatch_file_write(handle, &val, sizeof(gint64), err)) { return (FALSE); } return (TRUE); } /** * ipatch_file_buf_load: * @handle: File handle * @size: Size of data to load * @err: Location to store error info or %NULL * * Load data from a file into a buffer for error checking convenience. * I/O errors need only be checked on this function and not on the subsequent * buffered read function calls. It is an error if an end of file is * encountered before all the requested data is read. * * Returns: %TRUE on success, %FALSE on error. */ gboolean ipatch_file_buf_load(IpatchFileHandle *handle, guint size, GError **err) { g_return_val_if_fail(handle != NULL, FALSE); g_return_val_if_fail(size != 0, FALSE); /* If there is still buffered data, flush it (add to file position) */ if(handle->buf_position < handle->buf->len) { handle->position += handle->buf->len - handle->buf_position; } g_byte_array_set_size(handle->buf, size); handle->buf_position = 0; if(!_ipatch_file_read_no_pos_update(handle, handle->buf->data, size, err)) { return (FALSE); } return (TRUE); } /** * ipatch_file_buf_read: * @handle: File handle with loaded data to read from * @buf: Buffer to copy data to * @size: Amount of data to copy in bytes * * Read data from a file handle's buffer and advance the buffer's current * position. A call to ipatch_file_buf_load() must have been previously * executed and there must be enough remaining data in the buffer for the read. */ void ipatch_file_buf_read(IpatchFileHandle *handle, gpointer buf, guint size) { g_return_if_fail(handle != NULL); g_return_if_fail(handle->buf_position + size <= handle->buf->len); memcpy(buf, handle->buf->data + handle->buf_position, size); handle->buf_position += size; handle->position += size; } /** * ipatch_file_buf_write: * @handle: File handle to append buffered data to * @buf: Buffer to copy data from * @size: Amount of data to copy in bytes * * Write data to a file handle's buffer and advance the buffer's current * position. Buffer is expanded if necessary (since version 1.1.0). * Data will not actually be written to file till ipatch_file_buf_commit() is * called. */ void ipatch_file_buf_write(IpatchFileHandle *handle, gconstpointer buf, guint size) { g_return_if_fail(handle != NULL); if(size == 0) { return; } if(handle->buf_position + size > handle->buf->len) { g_byte_array_set_size(handle->buf, handle->buf_position + size); } memcpy(handle->buf->data + handle->buf_position, buf, size); handle->buf_position += size; handle->position += size; } /** * ipatch_file_buf_memset: * @handle: File handle to set buffered data of * @c: Character to write * @size: Size of data to set * * Sets the given @size in bytes to the character @c and advances the * current position. Buffer is expanded if necessary. */ void ipatch_file_buf_memset(IpatchFileHandle *handle, unsigned char c, guint size) { g_return_if_fail(handle != NULL); if(size == 0) { return; } if(handle->buf_position + size > handle->buf->len) { g_byte_array_set_size(handle->buf, handle->buf_position + size); } memset(handle->buf->data + handle->buf_position, c, size); handle->buf_position += size; handle->position += size; } /** * ipatch_file_buf_set_size: * @handle: File handle to adjust buffer of * @size: New size of buffer * * Sets the size of the buffer of @handle to @size bytes. The buffer is * expanded without initializing the newly allocated part or truncated * as necessary discarding any content over the new size. The current position * is updated to point to the end of the buffer if it would point outside the * new size of the buffer after truncating it. * * Since: 1.1.0 */ void ipatch_file_buf_set_size(IpatchFileHandle *handle, guint size) { g_return_if_fail(handle != NULL); if(size == handle->buf->len) { return; } g_byte_array_set_size(handle->buf, size); if(handle->buf_position > size) { handle->position += size - handle->buf_position; handle->buf_position = size; } } /** * ipatch_file_buf_commit: * @handle: File handle with buffered data to write * @err: Location to store error info or %NULL * * Writes all data in a file handle's buffer to the file and resets the * buffer to empty. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean ipatch_file_buf_commit(IpatchFileHandle *handle, GError **err) { g_return_val_if_fail(handle != NULL, FALSE); if(handle->buf->len == 0) { return (TRUE); /* no data? */ } if(!_ipatch_file_write_no_pos_update(handle, handle->buf->data, handle->buf->len, err)) { return (FALSE); } g_byte_array_set_size(handle->buf, 0); handle->buf_position = 0; return (TRUE); } /** * ipatch_file_buf_seek: * @handle: File handle * @offset: Offset to seek * @type: Seek type * * Seeks the current position in a file handle's buffer specified by an @offset * and seek @type. It is an error to seek outside of the current size of * buffered data (loaded or written). The @offset is relative to the buffer, * not the file position. */ void ipatch_file_buf_seek(IpatchFileHandle *handle, int offset, GSeekType type) { g_return_if_fail(handle != NULL); if(type == G_SEEK_CUR) { g_return_if_fail(handle->buf_position + offset >= 0); g_return_if_fail(handle->buf_position + offset < handle->buf->len); handle->buf_position += offset; handle->position += offset; } else if(type == G_SEEK_SET) { g_return_if_fail(offset >= 0 && (guint)offset < handle->buf->len); handle->position += offset - handle->buf_position; handle->buf_position = offset; } else if(type == G_SEEK_END) { g_return_if_fail(handle->buf->len + offset >= 0); g_return_if_fail(handle->buf->len + offset < handle->buf->len); handle->position = (handle->buf->len + offset) - handle->buf_position; handle->buf_position = handle->buf->len + offset; } } /** * ipatch_file_buf_read_u8: * @handle: File handle with loaded data * * Reads an unsigned byte from a file buffer and advances the buffer's * current position. * * Returns: The value. */ guint8 ipatch_file_buf_read_u8(IpatchFileHandle *handle) { guint8 *val; g_return_val_if_fail(handle != NULL, 0); g_return_val_if_fail(handle->buf_position + 1 <= handle->buf->len, 0); val = (guint8 *)(handle->buf->data + handle->buf_position); handle->buf_position++; handle->position++; return (*val); } /** * ipatch_file_buf_read_u16: * @handle: File handle with loaded data * * Reads an unsigned 16 bit word from a file buffer and advances the buffer's * current position. Performs endian byte swapping if necessary. * * Returns: The value. */ guint16 ipatch_file_buf_read_u16(IpatchFileHandle *handle) { guint16 *val; g_return_val_if_fail(handle != NULL, 0); g_return_val_if_fail(handle->buf_position + 2 <= handle->buf->len, 0); val = (guint16 *)(handle->buf->data + handle->buf_position); handle->buf_position += 2; handle->position += 2; return (IPATCH_FILE_SWAP16(handle->file, val)); } /** * ipatch_file_buf_read_u32: * @handle: File handle with loaded data * * Reads an unsigned 32 bit word from a file buffer and advances the buffer's * current position. Performs endian byte swapping if necessary. * * Returns: The value. */ guint32 ipatch_file_buf_read_u32(IpatchFileHandle *handle) { guint32 *val; g_return_val_if_fail(handle != NULL, 0); g_return_val_if_fail(handle->buf_position + 4 <= handle->buf->len, 0); val = (guint32 *)(handle->buf->data + handle->buf_position); handle->buf_position += 4; handle->position += 4; return (IPATCH_FILE_SWAP32(handle->file, val)); } /** * ipatch_file_buf_read_u64: * @handle: File handle with loaded data * * Reads an unsigned 64 bit word from a file buffer and advances the buffer's * current position. Performs endian byte swapping if necessary. * * Returns: The value. */ guint64 ipatch_file_buf_read_u64(IpatchFileHandle *handle) { guint64 *val; g_return_val_if_fail(handle != NULL, 0); g_return_val_if_fail(handle->buf_position + 8 <= handle->buf->len, 0); val = (guint64 *)(handle->buf->data + handle->buf_position); handle->buf_position += 8; handle->position += 8; return (IPATCH_FILE_SWAP64(handle->file, val)); } /** * ipatch_file_buf_read_s8: * @handle: File handle with loaded data * * Reads a signed byte from a file buffer and advances the buffer's * current position. * * Returns: The value. */ gint8 ipatch_file_buf_read_s8(IpatchFileHandle *handle) { gint8 *val; g_return_val_if_fail(handle != NULL, 0); g_return_val_if_fail(handle->buf_position + 1 <= handle->buf->len, 0); val = (gint8 *)(handle->buf->data + handle->buf_position); handle->buf_position++; handle->position++; return (*val); } /** * ipatch_file_buf_read_s16: * @handle: File handle with loaded data * * Reads a signed 16 bit word from a file buffer and advances the buffer's * current position. Performs endian byte swapping if necessary. * * Returns: The value. */ gint16 ipatch_file_buf_read_s16(IpatchFileHandle *handle) { gint16 *val; g_return_val_if_fail(handle != NULL, 0); g_return_val_if_fail(handle->buf_position + 2 <= handle->buf->len, 0); val = (gint16 *)(handle->buf->data + handle->buf_position); handle->buf_position += 2; handle->position += 2; return (IPATCH_FILE_SWAP16(handle->file, val)); } /** * ipatch_file_buf_read_s32: * @handle: File handle with loaded data * * Reads a signed 32 bit word from a file buffer and advances the buffer's * current position. Performs endian byte swapping if necessary. * * Returns: The value. */ gint32 ipatch_file_buf_read_s32(IpatchFileHandle *handle) { gint32 *val; g_return_val_if_fail(handle != NULL, 0); g_return_val_if_fail(handle->buf_position + 4 <= handle->buf->len, 0); val = (gint32 *)(handle->buf->data + handle->buf_position); handle->buf_position += 4; handle->position += 4; return (IPATCH_FILE_SWAP32(handle->file, val)); } /** * ipatch_file_buf_read_s64: * @handle: File handle with loaded data * * Reads a signed 64 bit word from a file buffer and advances the buffer's * current position. Performs endian byte swapping if necessary. * * Returns: The value. */ gint64 ipatch_file_buf_read_s64(IpatchFileHandle *handle) { gint64 *val; g_return_val_if_fail(handle != NULL, 0); g_return_val_if_fail(handle->buf_position + 8 <= handle->buf->len, 0); val = (gint64 *)(handle->buf->data + handle->buf_position); handle->buf_position += 8; handle->position += 8; return (IPATCH_FILE_SWAP64(handle->file, val)); } /** * ipatch_file_buf_write_u8: * @handle: File handle * @val: Value to write into file buffer * * Writes an unsigned byte to a file buffer and advances the buffer's * current position. The file buffer is expanded if needed. */ void ipatch_file_buf_write_u8(IpatchFileHandle *handle, guint8 val) { g_return_if_fail(handle != NULL); if(handle->buf_position + 1 > handle->buf->len) { g_byte_array_set_size(handle->buf, handle->buf_position + 1); } *(guint8 *)(handle->buf->data + handle->buf_position) = val; handle->buf_position++; handle->position++; } /** * ipatch_file_buf_write_u16: * @handle: File handle * @val: Value to write into file buffer * * Writes an unsigned 16 bit word to a file buffer and advances the buffer's * current position. Performs endian byte swapping if necessary. * The file buffer is expanded if needed. */ void ipatch_file_buf_write_u16(IpatchFileHandle *handle, guint16 val) { g_return_if_fail(handle != NULL); if(handle->buf_position + 2 > handle->buf->len) { g_byte_array_set_size(handle->buf, handle->buf_position + 2); } *(guint16 *)(handle->buf->data + handle->buf_position) = IPATCH_FILE_SWAP16(handle->file, &val); handle->buf_position += 2; handle->position += 2; } /** * ipatch_file_buf_write_u32: * @handle: File handle * @val: Value to write into file buffer * * Writes an unsigned 32 bit word to a file buffer and advances the buffer's * current position. Performs endian byte swapping if necessary. * The file buffer is expanded if needed. */ void ipatch_file_buf_write_u32(IpatchFileHandle *handle, guint32 val) { g_return_if_fail(handle != NULL); if(handle->buf_position + 4 > handle->buf->len) { g_byte_array_set_size(handle->buf, handle->buf_position + 4); } *(guint32 *)(handle->buf->data + handle->buf_position) = IPATCH_FILE_SWAP32(handle->file, &val); handle->buf_position += 4; handle->position += 4; } /** * ipatch_file_buf_write_u64: * @handle: File handle * @val: Value to write into file buffer * * Writes an unsigned 64 bit word to a file buffer and advances the buffer's * current position. Performs endian byte swapping if necessary. * The file buffer is expanded if needed. */ void ipatch_file_buf_write_u64(IpatchFileHandle *handle, guint64 val) { g_return_if_fail(handle != NULL); if(handle->buf_position + 8 > handle->buf->len) { g_byte_array_set_size(handle->buf, handle->buf_position + 8); } *(guint64 *)(handle->buf->data + handle->buf_position) = IPATCH_FILE_SWAP64(handle->file, &val); handle->buf_position += 8; handle->position += 8; } /** * ipatch_file_buf_write_s8: * @handle: File handle * @val: Value to write into file buffer * * Writes a signed byte to a file buffer and advances the buffer's * current position. The file buffer is expanded if needed. */ void ipatch_file_buf_write_s8(IpatchFileHandle *handle, gint8 val) { g_return_if_fail(handle != NULL); if(handle->buf_position + 1 > handle->buf->len) { g_byte_array_set_size(handle->buf, handle->buf_position + 1); } *(gint8 *)(handle->buf->data + handle->buf_position) = val; handle->buf_position++; handle->position++; } /** * ipatch_file_buf_write_s16: * @handle: File handle * @val: Value to write into file buffer * * Writes a signed 16 bit word to a file buffer and advances the buffer's * current position. Performs endian byte swapping if necessary. * The file buffer is expanded if needed. */ void ipatch_file_buf_write_s16(IpatchFileHandle *handle, gint16 val) { g_return_if_fail(handle != NULL); if(handle->buf_position + 2 > handle->buf->len) { g_byte_array_set_size(handle->buf, handle->buf_position + 2); } *(gint16 *)(handle->buf->data + handle->buf_position) = IPATCH_FILE_SWAP16(handle->file, &val); handle->buf_position += 2; handle->position += 2; } /** * ipatch_file_buf_write_s32: * @handle: File handle * @val: Value to write into file buffer * * Writes a signed 32 bit word to a file buffer and advances the buffer's * current position. Performs endian byte swapping if necessary. * The file buffer is expanded if needed. */ void ipatch_file_buf_write_s32(IpatchFileHandle *handle, gint32 val) { g_return_if_fail(handle != NULL); if(handle->buf_position + 4 > handle->buf->len) { g_byte_array_set_size(handle->buf, handle->buf_position + 4); } *(gint32 *)(handle->buf->data + handle->buf_position) = IPATCH_FILE_SWAP32(handle->file, &val); handle->buf_position += 4; handle->position += 4; } /** * ipatch_file_buf_write_s64: * @handle: File handle * @val: Value to write into file buffer * * Writes a signed 64 bit word to a file buffer and advances the buffer's * current position. Performs endian byte swapping if necessary. * The file buffer is expanded if needed. */ void ipatch_file_buf_write_s64(IpatchFileHandle *handle, gint64 val) { g_return_if_fail(handle != NULL); if(handle->buf_position + 8 > handle->buf->len) { g_byte_array_set_size(handle->buf, handle->buf_position + 8); } *(gint64 *)(handle->buf->data + handle->buf_position) = IPATCH_FILE_SWAP64(handle->file, &val); handle->buf_position += 8; handle->position += 8; } libinstpatch-1.1.6/libinstpatch/IpatchGig.c000066400000000000000000000107551400263525300207020ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchGig * @short_description: GigaSampler instrument file object * @see_also: * @stability: Stable * * Defines a GigaSampler instrument file object. */ #include #include #include "IpatchGig.h" #include "IpatchGigInst.h" #include "IpatchGigSample.h" #include "IpatchContainer.h" #include "IpatchVirtualContainer_types.h" #include "i18n.h" static void ipatch_gig_class_init(IpatchGigClass *klass); static void ipatch_gig_finalize(GObject *object); static void ipatch_gig_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static const GType *ipatch_gig_container_child_types(void); static const GType *ipatch_gig_container_virtual_types(void); static GObjectClass *parent_class = NULL; static GType gig_child_types[3] = { 0 }; static GType gig_virt_types[4] = { 0 }; GType ipatch_gig_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchGigClass), NULL, NULL, (GClassInitFunc) ipatch_gig_class_init, NULL, NULL, sizeof(IpatchGig), 0, (GInstanceInitFunc) NULL, }; item_type = g_type_register_static(IPATCH_TYPE_DLS2, "IpatchGig", &item_info, 0); } return (item_type); } static void ipatch_gig_class_init(IpatchGigClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->finalize = ipatch_gig_finalize; item_class->copy = ipatch_gig_item_copy; container_class->child_types = ipatch_gig_container_child_types; container_class->virtual_types = ipatch_gig_container_virtual_types; gig_child_types[0] = IPATCH_TYPE_GIG_INST; gig_child_types[1] = IPATCH_TYPE_GIG_SAMPLE; gig_virt_types[0] = IPATCH_TYPE_VIRTUAL_GIG_MELODIC; gig_virt_types[1] = IPATCH_TYPE_VIRTUAL_GIG_PERCUSSION; gig_virt_types[2] = IPATCH_TYPE_VIRTUAL_GIG_SAMPLES; } static void ipatch_gig_finalize(GObject *object) { IpatchGig *gig = IPATCH_GIG(object); GSList *p; IPATCH_ITEM_WLOCK(object); /* free sample group names */ p = gig->group_names; while(p) { g_free(p->data); p = g_slist_delete_link(p, p); } IPATCH_ITEM_WUNLOCK(object); parent_class->finalize(object); } static void ipatch_gig_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchGig *src_gig, *dest_gig; GSList *p; src_gig = IPATCH_GIG(src); dest_gig = IPATCH_GIG(dest); IPATCH_ITEM_CLASS(parent_class)->copy(dest, src, link_func, user_data); IPATCH_ITEM_RLOCK(src_gig); /* duplicate group names */ p = src_gig->group_names; while(p) { dest_gig->group_names = g_slist_append(dest_gig->group_names, g_strdup((char *)(p->data))); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(src_gig); } static const GType * ipatch_gig_container_child_types(void) { return (gig_child_types); } static const GType * ipatch_gig_container_virtual_types(void) { return (gig_virt_types); } /** * ipatch_gig_new: * * Create a new GigaSampler object. * * Returns: New GigaSampler object with a ref count of 1 which the caller * owns. */ IpatchGig * ipatch_gig_new(void) { return (IPATCH_GIG(g_object_new(IPATCH_TYPE_GIG, NULL))); } libinstpatch-1.1.6/libinstpatch/IpatchGig.h000066400000000000000000000041021400263525300206740ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_GIG_H__ #define __IPATCH_GIG_H__ #include #include /* NOTE: See IpatchGigFile.h for some info on GigaSampler file format */ typedef struct _IpatchGig IpatchGig; typedef struct _IpatchGigClass IpatchGigClass; #include #include #define IPATCH_TYPE_GIG (ipatch_gig_get_type ()) #define IPATCH_GIG(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_GIG, IpatchGig)) #define IPATCH_GIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_GIG, IpatchGigClass)) #define IPATCH_IS_GIG(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_GIG)) #define IPATCH_IS_GIG_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_GIG)) #define IPATCH_GIG_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_GIG, IpatchGigClass)) /* GigaSampler object */ struct _IpatchGig { IpatchDLS2 parent_instance; /* derived from DLS2 object */ GSList *group_names; /* sample group names */ }; struct _IpatchGigClass { IpatchDLS2Class parent_class; }; /* Default GigaSampler sample group name */ #define IPATCH_GIG_DEFAULT_SAMPLE_GROUP_NAME "Default Sample Group" GType ipatch_gig_get_type(void); IpatchGig *ipatch_gig_new(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchGigDimension.c000066400000000000000000000202301400263525300225350ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchGigDimension * @short_description: GigaSampler dimension object * @see_also: #IpatchGigInst * @stability: Stable * * Defines a GigaSampler dimension object which are the children of * #IpatchGigInst objects. */ #include #include #include "IpatchGigDimension.h" #include "IpatchRange.h" #include "IpatchTypeProp.h" #include "ipatch_priv.h" #include "builtin_enums.h" enum { PROP_0, PROP_NAME, PROP_TYPE, PROP_SPLIT_COUNT }; static void ipatch_gig_dimension_class_init(IpatchGigDimensionClass *klass); static void ipatch_gig_dimension_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_gig_dimension_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_gig_dimension_init(IpatchGigDimension *gig_dimension); static void ipatch_gig_dimension_finalize(GObject *gobject); static void ipatch_gig_dimension_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static GObjectClass *parent_class = NULL; GType ipatch_gig_dimension_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchGigDimensionClass), NULL, NULL, (GClassInitFunc)ipatch_gig_dimension_class_init, NULL, NULL, sizeof(IpatchGigDimension), 0, (GInstanceInitFunc)ipatch_gig_dimension_init, }; item_type = g_type_register_static(IPATCH_TYPE_ITEM, "IpatchGigDimension", &item_info, 0); } return (item_type); } static void ipatch_gig_dimension_class_init(IpatchGigDimensionClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->finalize = ipatch_gig_dimension_finalize; obj_class->get_property = ipatch_gig_dimension_get_property; item_class->item_set_property = ipatch_gig_dimension_set_property; item_class->copy = ipatch_gig_dimension_item_copy; /* use parent's mutex (IpatchGigRegion) */ item_class->mutex_slave = TRUE; g_object_class_override_property(obj_class, PROP_NAME, "title"); g_object_class_install_property(obj_class, PROP_NAME, g_param_spec_string("name", _("Name"), _("Dimension name"), NULL, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_TYPE, g_param_spec_enum("type", _("Type"), _("Dimension type"), IPATCH_TYPE_GIG_DIMENSION_TYPE, IPATCH_GIG_DIMENSION_NONE, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_TYPE, g_param_spec_int("split-count", _("Split count"), _("Number of split bits"), 1, 5, 1, G_PARAM_READWRITE)); } static void ipatch_gig_dimension_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchGigDimension *dimension = IPATCH_GIG_DIMENSION(object); switch(property_id) { case PROP_NAME: IPATCH_ITEM_WLOCK(dimension); g_free(dimension->name); dimension->name = g_value_dup_string(value); IPATCH_ITEM_WUNLOCK(dimension); /* title property notify */ ipatch_item_prop_notify((IpatchItem *)dimension, ipatch_item_pspec_title, value, NULL); break; case PROP_TYPE: dimension->type = g_value_get_enum(value); break; case PROP_SPLIT_COUNT: dimension->split_count = g_value_get_int(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_gig_dimension_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchGigDimension *dimension = IPATCH_GIG_DIMENSION(object); switch(property_id) { case PROP_NAME: IPATCH_ITEM_RLOCK(dimension); g_value_set_string(value, dimension->name); IPATCH_ITEM_RUNLOCK(dimension); break; case PROP_TYPE: g_value_set_enum(value, dimension->type); break; case PROP_SPLIT_COUNT: g_value_set_int(value, dimension->split_count); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_gig_dimension_init(IpatchGigDimension *dimension) { } static void ipatch_gig_dimension_finalize(GObject *gobject) { IpatchGigDimension *dimension = IPATCH_GIG_DIMENSION(gobject); IPATCH_ITEM_WLOCK(dimension); g_free(dimension->name); dimension->name = NULL; IPATCH_ITEM_WUNLOCK(dimension); if(parent_class->finalize) { parent_class->finalize(gobject); } } static void ipatch_gig_dimension_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchGigDimension *src_dim, *dest_dim; src_dim = IPATCH_GIG_DIMENSION(src); dest_dim = IPATCH_GIG_DIMENSION(dest); IPATCH_ITEM_RLOCK(src_dim); dest_dim->name = g_strdup(src_dim->name); dest_dim->type = src_dim->type; dest_dim->split_count = src_dim->split_count; dest_dim->split_mask = src_dim->split_mask; dest_dim->split_shift = src_dim->split_shift; IPATCH_ITEM_RUNLOCK(src_dim); } /** * ipatch_gig_dimension_new: * * Create a new GigaSampler instrument dimension. * * Returns: New GigaSampler dimension with a ref count of 1 which the caller * owns. */ IpatchGigDimension * ipatch_gig_dimension_new(void) { return (IPATCH_GIG_DIMENSION(g_object_new(IPATCH_TYPE_GIG_DIMENSION, NULL))); } /** * ipatch_gig_dimension_first: (skip) * @iter: Patch item iterator containing #IpatchGigDimension items * * Gets the first item in a dimension iterator. A convenience * wrapper for ipatch_iter_first(). * * Returns: The first dimension in @iter or %NULL if empty. */ IpatchGigDimension * ipatch_gig_dimension_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_GIG_DIMENSION(obj)); } else { return (NULL); } } /** * ipatch_gig_dimension_next: (skip) * @iter: Patch item iterator containing #IpatchGigDimension items * * Gets the next item in a dimension iterator. A convenience wrapper * for ipatch_iter_next(). * * Returns: The next dimension in @iter or %NULL if at the end of * the list. */ IpatchGigDimension * ipatch_gig_dimension_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_GIG_DIMENSION(obj)); } else { return (NULL); } } libinstpatch-1.1.6/libinstpatch/IpatchGigDimension.h000066400000000000000000000114011400263525300225420ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_GIG_DIMENSION_H__ #define __IPATCH_GIG_DIMENSION_H__ #include #include /* forward type declarations */ typedef struct _IpatchGigDimension IpatchGigDimension; typedef struct _IpatchGigDimensionClass IpatchGigDimensionClass; /* GigaSampler dimension type */ typedef enum { IPATCH_GIG_DIMENSION_NONE = 0x00, /* not in use (is this in files?) */ /* MIDI controllers - see IpatchGigControlType (IpatchGigEffects.h) */ IPATCH_GIG_DIMENSION_MOD_WHEEL = 0x01, IPATCH_GIG_DIMENSION_BREATH = 0x02, IPATCH_GIG_DIMENSION_FOOT = 0x04, IPATCH_GIG_DIMENSION_PORTAMENTO_TIME = 0x05, IPATCH_GIG_DIMENSION_EFFECT_1 = 0x0C, IPATCH_GIG_DIMENSION_EFFECT_2 = 0x0D, IPATCH_GIG_DIMENSION_GEN_PURPOSE_1 = 0x10, IPATCH_GIG_DIMENSION_GEN_PURPOSE_2 = 0x11, IPATCH_GIG_DIMENSION_GEN_PURPOSE_3 = 0x12, IPATCH_GIG_DIMENSION_GEN_PURPOSE_4 = 0x13, IPATCH_GIG_DIMENSION_SUSTAIN_PEDAL = 0x40, IPATCH_GIG_DIMENSION_PORTAMENTO = 0x41, IPATCH_GIG_DIMENSION_SOSTENUTO = 0x42, IPATCH_GIG_DIMENSION_SOFT_PEDAL = 0x43, IPATCH_GIG_DIMENSION_GEN_PURPOSE_5 = 0x50, IPATCH_GIG_DIMENSION_GEN_PURPOSE_6 = 0x51, IPATCH_GIG_DIMENSION_GEN_PURPOSE_7 = 0x52, IPATCH_GIG_DIMENSION_GEN_PURPOSE_8 = 0x53, IPATCH_GIG_DIMENSION_EFFECT_DEPTH_1 = 0x5B, IPATCH_GIG_DIMENSION_EFFECT_DEPTH_2 = 0x5C, IPATCH_GIG_DIMENSION_EFFECT_DEPTH_3 = 0x5D, IPATCH_GIG_DIMENSION_EFFECT_DEPTH_4 = 0x5E, IPATCH_GIG_DIMENSION_EFFECT_DEPTH_5 = 0x5F, IPATCH_GIG_DIMENSION_CHANNEL = 0x80, /* sample has more than 1 channel */ IPATCH_GIG_DIMENSION_LAYER = 0x81, /* layer up to 8 zones (cross fade 2 or 4) */ IPATCH_GIG_DIMENSION_VELOCITY = 0x82, /* key velocity (only type that allows specific ranges) */ IPATCH_GIG_DIMENSION_AFTER_TOUCH = 0x83, /* channel MIDI after touch */ IPATCH_GIG_DIMENSION_RELEASE_TRIG = 0x84, /* trigger on key release */ IPATCH_GIG_DIMENSION_KEYBOARD = 0x85, /* key switching (FIXME WTF?) */ IPATCH_GIG_DIMENSION_ROUND_ROBIN = 0x86, /* selects zones in sequence */ IPATCH_GIG_DIMENSION_RANDOM = 0x87 /* selects random zone */ } IpatchGigDimensionType; /* maximum value for dimension type */ #define IPATCH_GIG_DIMENSION_TYPE_MAX IPATCH_GIG_DIMENSION_RANDOM #include #include #define IPATCH_TYPE_GIG_DIMENSION (ipatch_gig_dimension_get_type ()) #define IPATCH_GIG_DIMENSION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_GIG_DIMENSION, \ IpatchGigDimension)) #define IPATCH_GIG_DIMENSION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_GIG_DIMENSION, \ IpatchGigDimensionClass)) #define IPATCH_IS_GIG_DIMENSION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_GIG_DIMENSION)) #define IPATCH_IS_GIG_DIMENSION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_GIG_DIMENSION)) #define IPATCH_GIG_DIMENSION_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_GIG_DIMENSION, \ IpatchGigDimensionClass)) /* GigaSampler dimension (up to 5 per IpatchGigRegion) */ struct _IpatchGigDimension { IpatchItem parent_instance; char *name; /* name of dimension or NULL */ guint8 type; /* dimension type (IpatchGigDimensionType) */ guint8 split_count; /* count of split bits for this dimension */ /* convenience variables (derivable from other info) */ guint8 split_mask; /* sub region index mask */ guint8 split_shift; /* bit shift to first set bit in mask */ }; /* GigaSampler dimension class */ struct _IpatchGigDimensionClass { IpatchItemClass parent_class; }; GType ipatch_gig_dimension_get_type(void); IpatchGigDimension *ipatch_gig_dimension_new(void); IpatchGigDimension *ipatch_gig_dimension_first(IpatchIter *iter); IpatchGigDimension *ipatch_gig_dimension_next(IpatchIter *iter); #endif libinstpatch-1.1.6/libinstpatch/IpatchGigEffects.c000066400000000000000000000346601400263525300222030ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchGigEffects * @short_description: GigaSampler instrument parameters and effects * @see_also: * @stability: Stable * * Functions and types for GigaSampler instrument parameters and effects. */ #include #include #include #include "IpatchGigEffects.h" /** * ipatch_gig_parse_effects: * @handle: File handle containing buffered 3ewa data * @effects: (out): Pointer to a user supplied GigaSampler effects structure to fill * * Parse an 3ewa GigaSampler effects chunk into a structure. */ void ipatch_gig_parse_effects(IpatchFileHandle *handle, IpatchGigEffects *effects) { g_return_if_fail(handle != NULL); g_return_if_fail(effects != NULL); effects->unknown1 = ipatch_file_buf_read_u32(handle); /* 0-4 */ effects->lfo3_freq = ipatch_file_buf_read_u32(handle); effects->eg3_attack = ipatch_file_buf_read_u32(handle); effects->unknown2 = ipatch_file_buf_read_u16(handle); /* 12-13 */ effects->lfo1_internal_depth = ipatch_file_buf_read_u16(handle); effects->unknown3 = ipatch_file_buf_read_u16(handle); /* 16-17 */ effects->lfo3_internal_depth = ipatch_file_buf_read_u16(handle); effects->unknown4 = ipatch_file_buf_read_u16(handle); /* 20-21 */ effects->lfo1_ctrl_depth = ipatch_file_buf_read_u16(handle); effects->unknown5 = ipatch_file_buf_read_u16(handle); /* 22-23 */ effects->lfo3_ctrl_depth = ipatch_file_buf_read_u16(handle); effects->eg1_attack = ipatch_file_buf_read_u32(handle); effects->eg1_decay = ipatch_file_buf_read_u32(handle); effects->unknown6 = ipatch_file_buf_read_u16(handle); /* 36-37 */ effects->eg1_sustain = ipatch_file_buf_read_u16(handle); effects->eg1_release = ipatch_file_buf_read_u32(handle); effects->unknown7 = ipatch_file_buf_read_u32(handle); /* 44-47 */ effects->lfo1_freq = ipatch_file_buf_read_u32(handle); effects->eg2_attack = ipatch_file_buf_read_u32(handle); effects->eg2_decay = ipatch_file_buf_read_u32(handle); effects->unknown8 = ipatch_file_buf_read_u16(handle); /* 60-61 */ effects->eg2_sustain = ipatch_file_buf_read_u16(handle); effects->eg2_release = ipatch_file_buf_read_u32(handle); effects->unknown9 = ipatch_file_buf_read_u16(handle); /* 68-69 */ effects->lfo2_ctrl_depth = ipatch_file_buf_read_u16(handle); effects->lfo2_freq = ipatch_file_buf_read_u32(handle); effects->unknown10 = ipatch_file_buf_read_u16(handle); /* 76-77 */ effects->lfo2_internal_depth = ipatch_file_buf_read_u16(handle); effects->eg1_decay2 = ipatch_file_buf_read_u32(handle); effects->unknown11 = ipatch_file_buf_read_u16(handle); /* 84-85 */ effects->eg1_pre_attack = ipatch_file_buf_read_u16(handle); effects->eg2_decay2 = ipatch_file_buf_read_u32(handle); effects->turbo_lowpass = ipatch_file_buf_read_u8(handle); effects->unknown12 = ipatch_file_buf_read_u8(handle); /* 93 */ effects->eg2_pre_attack = ipatch_file_buf_read_u16(handle); effects->vel_response = ipatch_file_buf_read_u8(handle); effects->release_vel_response = ipatch_file_buf_read_u8(handle); effects->unknown13 = ipatch_file_buf_read_u16(handle); /* 98-99 */ effects->unknown14 = ipatch_file_buf_read_u32(handle); /* 100-103 */ effects->sample_offset = ipatch_file_buf_read_u16(handle); effects->unknown15 = ipatch_file_buf_read_u16(handle); /* 106-107 */ effects->pitch_track_dim_bypass = ipatch_file_buf_read_u8(handle); effects->layer_pan = ipatch_file_buf_read_u8(handle); effects->self_mask = ipatch_file_buf_read_u8(handle); effects->unknown16 = ipatch_file_buf_read_u8(handle); /* 111 */ effects->lfo3_ctrl = ipatch_file_buf_read_u8(handle); effects->attn_ctrl = ipatch_file_buf_read_u8(handle); effects->lfo2_ctrl = ipatch_file_buf_read_u8(handle); effects->lfo1_ctrl = ipatch_file_buf_read_u8(handle); effects->unknown17 = ipatch_file_buf_read_u16(handle); /* 116-117 */ effects->lfo3_internal_depth = ipatch_file_buf_read_u16(handle); effects->channel_offset = ipatch_file_buf_read_u8(handle); effects->sust_defeat = ipatch_file_buf_read_u8(handle); effects->unknown18 = ipatch_file_buf_read_u16(handle); /* 122-123 */ effects->max_velocity = ipatch_file_buf_read_u8(handle); effects->unknown19 = ipatch_file_buf_read_u8(handle); /* 125 */ effects->unknown20 = ipatch_file_buf_read_u16(handle); /* 126-127 */ effects->release_trigger_decay = ipatch_file_buf_read_u8(handle); effects->unknown21 = ipatch_file_buf_read_u8(handle); /* 129 */ effects->unknown22 = ipatch_file_buf_read_u8(handle); /* 130 */ effects->eg1_hold = ipatch_file_buf_read_u8(handle); effects->filter_cutoff = ipatch_file_buf_read_u8(handle); effects->filter_midi_ctrl = ipatch_file_buf_read_u8(handle); effects->filter_vel_scale = ipatch_file_buf_read_u8(handle); effects->unknown23 = ipatch_file_buf_read_u8(handle); /* 135 */ effects->filter_resonance = ipatch_file_buf_read_u8(handle); effects->filter_breakpoint = ipatch_file_buf_read_u8(handle); effects->vel_dyn_range = ipatch_file_buf_read_u8(handle); effects->filter_type = ipatch_file_buf_read_u8(handle); } /** * ipatch_gig_write_effects: * @handle: File handle to buffer writes to, should be committed after this call * @effects: Pointer to GigaSampler effects structure to store * * Store a 3ewa GigaSampler effects chunk into a file buffer. The file * buffer should be at least #IPATCH_GIG_3EWA_SIZE bytes. */ void ipatch_gig_store_effects(IpatchFileHandle *handle, IpatchGigEffects *effects) { g_return_if_fail(handle != NULL); g_return_if_fail(effects != NULL); ipatch_file_buf_write_u32(handle, effects->unknown1); /* 0-3 */ ipatch_file_buf_write_u32(handle, effects->lfo3_freq); ipatch_file_buf_write_u32(handle, effects->eg3_attack); ipatch_file_buf_write_u16(handle, effects->unknown2); /* 12-13 */ ipatch_file_buf_write_u16(handle, effects->lfo1_internal_depth); ipatch_file_buf_write_u16(handle, effects->unknown3); /* 16-17 unknown */ ipatch_file_buf_write_u16(handle, effects->lfo3_internal_depth); ipatch_file_buf_write_u16(handle, effects->unknown4); /* 20-21 unknown */ ipatch_file_buf_write_u16(handle, effects->lfo1_ctrl_depth); ipatch_file_buf_write_u16(handle, effects->unknown5); /* 24-25 unknown */ ipatch_file_buf_write_u16(handle, effects->lfo3_ctrl_depth); ipatch_file_buf_write_u32(handle, effects->eg1_attack); ipatch_file_buf_write_u32(handle, effects->eg1_decay); ipatch_file_buf_write_u16(handle, effects->unknown6); /* 36-37 unknown */ ipatch_file_buf_write_u16(handle, effects->eg1_sustain); ipatch_file_buf_write_u32(handle, effects->eg1_release); ipatch_file_buf_write_u32(handle, effects->unknown7); /* 44-47 unknown */ ipatch_file_buf_write_u32(handle, effects->lfo1_freq); ipatch_file_buf_write_u32(handle, effects->eg2_attack); ipatch_file_buf_write_u32(handle, effects->eg2_decay); ipatch_file_buf_write_u16(handle, effects->unknown8); /* 60-61 unknown */ ipatch_file_buf_write_u16(handle, effects->eg2_sustain); ipatch_file_buf_write_u32(handle, effects->eg2_release); ipatch_file_buf_write_u16(handle, effects->unknown9); /* 68-69 unknown */ ipatch_file_buf_write_u16(handle, effects->lfo2_ctrl_depth); ipatch_file_buf_write_u32(handle, effects->lfo2_freq); ipatch_file_buf_write_u16(handle, effects->unknown10); /* 76-77 unknown */ ipatch_file_buf_write_u16(handle, effects->lfo2_internal_depth); ipatch_file_buf_write_u32(handle, effects->eg1_decay2); ipatch_file_buf_write_u16(handle, effects->unknown11); /* 84-85 unknown */ ipatch_file_buf_write_u16(handle, effects->eg1_pre_attack); ipatch_file_buf_write_u32(handle, effects->eg2_decay2); ipatch_file_buf_write_u8(handle, effects->turbo_lowpass); ipatch_file_buf_write_u8(handle, effects->unknown12); /* 93 unknown */ ipatch_file_buf_write_u16(handle, effects->eg2_pre_attack); ipatch_file_buf_write_u8(handle, effects->vel_response); ipatch_file_buf_write_u8(handle, effects->release_vel_response); ipatch_file_buf_write_u16(handle, effects->unknown13); /* 98-99 unknown */ ipatch_file_buf_write_u32(handle, effects->unknown14); /* 100-103 unknown */ ipatch_file_buf_write_u16(handle, effects->sample_offset); ipatch_file_buf_write_u16(handle, effects->unknown15); /* 106-107 unknown */ ipatch_file_buf_write_u8(handle, effects->pitch_track_dim_bypass); ipatch_file_buf_write_u8(handle, effects->layer_pan); ipatch_file_buf_write_u8(handle, effects->self_mask); ipatch_file_buf_write_u8(handle, effects->unknown16); /* 111 unknown */ ipatch_file_buf_write_u8(handle, effects->lfo3_ctrl); ipatch_file_buf_write_u8(handle, effects->attn_ctrl); ipatch_file_buf_write_u8(handle, effects->lfo2_ctrl); ipatch_file_buf_write_u8(handle, effects->lfo1_ctrl); ipatch_file_buf_write_u16(handle, effects->unknown17); /* 116-117 unknown */ ipatch_file_buf_write_u16(handle, effects->lfo3_internal_depth); ipatch_file_buf_write_u8(handle, effects->channel_offset); ipatch_file_buf_write_u8(handle, effects->sust_defeat); ipatch_file_buf_write_u16(handle, effects->unknown18); /* 122-123 unknown */ ipatch_file_buf_write_u8(handle, effects->max_velocity); ipatch_file_buf_write_u8(handle, effects->unknown19); /* 125 unknown */ ipatch_file_buf_write_u16(handle, effects->unknown20); /* 126-127 unknown */ ipatch_file_buf_write_u8(handle, effects->release_trigger_decay); ipatch_file_buf_write_u8(handle, effects->unknown21); /* 129 unknown */ ipatch_file_buf_write_u8(handle, effects->unknown22); /* 130 unknown */ ipatch_file_buf_write_u8(handle, effects->eg1_hold); ipatch_file_buf_write_u8(handle, effects->filter_cutoff); ipatch_file_buf_write_u8(handle, effects->filter_midi_ctrl); ipatch_file_buf_write_u8(handle, effects->filter_vel_scale); ipatch_file_buf_write_u8(handle, effects->unknown23); /* 135 unknown */ ipatch_file_buf_write_u8(handle, effects->filter_resonance); ipatch_file_buf_write_u8(handle, effects->filter_breakpoint); ipatch_file_buf_write_u8(handle, effects->vel_dyn_range); ipatch_file_buf_write_u8(handle, effects->filter_type); } /** * ipatch_gig_effects_init: * @effects: GigaSampler effects structure * * Initialize GigaSampler effects structure to default values. */ void ipatch_gig_effects_init(IpatchGigEffects *effects) { memset(effects, 0, sizeof(IpatchGigEffects)); /* FIXME - initialize to defaults */ } /* * GigaSampler has independent Volume/Pitch/Filter envelopes and LFOs where * as SoundFont has a Volume Envelope and combined Pitch/Filter envelope, * and a combined Volume/Filter/Pitch LFO and a second Pitch LFO. * * - Filter parameters are only activated if the filter is of type lowpass. * - Filter envelope parameters take precedence over Pitch envelope * - Second SoundFont pitch LFO is always used for Gig pitch LFO * - Volume LFO parameters take precedence over filter parameters */ /** * ipatch_gig_effects_to_gen_array: * @effects: GigaSampler effects structure * @array: SoundFont generator array * * Convert GigaSampler effects structure to SoundFont generator array */ void ipatch_gig_effects_to_gen_array(IpatchGigEffects *effects, IpatchSF2GenArray *array) { guint64 set_vals = IPATCH_SF2_GENID_SET(IPATCH_SF2_GEN_VOL_ENV_ATTACK) | IPATCH_SF2_GENID_SET(IPATCH_SF2_GEN_VOL_ENV_DECAY) | IPATCH_SF2_GENID_SET(IPATCH_SF2_GEN_VOL_ENV_RELEASE); IpatchSF2GenAmount *vals = array->values; array->flags |= set_vals; /* volume envelope */ /* can we do something with the pre-attack level? */ vals[IPATCH_SF2_GEN_VOL_ENV_ATTACK].sword = ipatch_gig_to_sf2_timecents(effects->eg1_attack); /* GigaSampler doesn't have a hold stage, only a hold until loop toggle */ /* FIXME - we could calculate time until loop */ vals[IPATCH_SF2_GEN_VOL_ENV_DECAY].sword = ipatch_gig_to_sf2_timecents(effects->eg1_decay); /* can we do something with decay2? */ /* vals[IPATCH_SF2_GEN_VOL_ENV_SUSTAIN].sword = ipatch_gig_volsust_to_sf2_centibels (effects->eg1_sustain); */ vals[IPATCH_SF2_GEN_VOL_ENV_RELEASE].sword = ipatch_gig_to_sf2_timecents(effects->eg1_release); /* filter envelope */ #if 0 /* only use filter parameters if its of lowpass type */ if(effects->filter_type == IPATCH_GIG_FILTER_LOWPASS) { vals[IPATCH_SF2_GEN_MOD_ENV_ATTACK].sword = ipatch_gig_to_sf2_timecents(effects->eg2_attack); vals[IPATCH_SF2_GEN_MOD_ENV_DECAY].sword = ipatch_gig_to_sf2_timecents(effects->eg2_decay); /* can we do something with decay2? */ vals[IPATCH_SF2_GEN_VOL_ENV_SUSTAIN].sword = effects->eg1_sustain; vals[IPATCH_SF2_GEN_VOL_ENV_RELEASE].sword = ipatch_gig_to_sf2_timecents(effects->eg1_release); /* filter LFO */ } #endif } /** * ipatch_gig_to_sf2_timecents: * @gig_tc: Amount in GigaSampler timecents * * Convert GigaSampler timecents to SoundFont timecents. * * Returns: SoundFont timecent value */ guint16 ipatch_gig_to_sf2_timecents(gint32 gig_tc) { return (gig_tc >> 16); /* divide by 65536 */ } /** * ipatch_gig_volsust_to_sf2_centibels: * @gig_tperc: Tenth percent sustain level * * Convert GigaSampler volume sustain (tenth percent units) to * centibels (10th of a decibel). * * Returns: SoundFont centibels value */ guint16 ipatch_gig_volsust_to_sf2_centibels(guint gig_tperc) { gig_tperc = CLAMP(gig_tperc, 0, 1000); return (1000 - gig_tperc); /* FIXME - probably wrong! */ } libinstpatch-1.1.6/libinstpatch/IpatchGigEffects.h000066400000000000000000000153161400263525300222050ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_GIG_EFFECTS_H__ #define __IPATCH_GIG_EFFECTS_H__ #include #include typedef struct _IpatchGigEffects IpatchGigEffects; #include #include /* GigaSampler envelope/lfo/filter settings (3ewa chunk) */ struct _IpatchGigEffects { guint32 unknown1; /* byte 0-3, always 0x0000008C? */ guint32 unknown7; /* byte 44-47 */ guint32 unknown14; /* byte 100-103 */ guint16 unknown2; /* byte 12-13 */ guint16 unknown3; /* byte 16-17 */ guint16 unknown4; /* byte 20-21 */ guint16 unknown5; /* byte 24-25 */ guint16 unknown6; /* byte 36-37 */ guint16 unknown8; /* byte 60-61 */ guint16 unknown9; /* byte 68-69 */ guint16 unknown10; /* byte 76-77 */ guint16 unknown11; /* byte 84-85 */ guint16 unknown13; /* byte 98-99 */ guint16 unknown15; /* byte 106-107 */ guint16 unknown17; /* byte 116-117 */ guint16 unknown18; /* byte 122-123 */ guint16 unknown20; /* byte 126-127 */ guint8 unknown12; /* byte 93 */ guint8 unknown16; /* byte 111 */ guint8 unknown19; /* byte 125 */ guint8 unknown21; /* byte 129 */ guint8 unknown22; /* byte 130 */ guint8 unknown23; /* byte 135 */ /* EG1 - Volume envelope */ guint16 eg1_pre_attack; /* 10th percent */ guint16 eg1_sustain; /* 10th percent */ guint32 eg1_attack; /* timecents */ guint32 eg1_decay; /* timecents */ guint32 eg1_decay2; /* timecents (where is the "inf" flag?) */ guint32 eg1_release; /* timecents */ guint8 eg1_hold; /* bit 8=1:true */ /* EG2 - Filter envelope */ guint16 eg2_pre_attack; /* 10th percent */ guint16 eg2_sustain; /* 10th percent */ guint32 eg2_attack; /* timecents */ guint32 eg2_decay; /* timecents */ guint32 eg2_decay2; /* timecents */ guint32 eg2_release; /* timecents */ /* EG3 - Pitch envelope */ guint32 eg3_attack; /* timecents */ guint16 eg3_depth; /* 12 bit signed (cents) */ /* LFO1 - Volume LFO */ guint16 lfo1_internal_depth; /* 0-1200 */ guint32 lfo1_freq; /* pitch cents */ guint16 lfo1_ctrl_depth; /* 0-1200 */ guint8 lfo1_ctrl; /* 0=internal, 1=mod wheel, 2=breath ctrl, 3=internal/mod wheel, 4=internal/breath ctrl */ /* bit 8=1:flip phase */ /* bit 7=1:synch */ /* bits 5 en 6: Res midictrl: 0=18, 1=19, 2=80, 3=81 */ /* LFO2 - Filter LFO */ guint8 lfo2_ctrl; /* 0=internal, 1=mod wheel, 2=breath ctrl, 3=internal/mod wheel, 4=internal/breath ctrl */ /* bit 6=1:synch */ /* bit 8=1:flip phase */ /* bit 7=1:Resonance midi ctrl */ guint32 lfo2_freq; /* pitch cents */ guint16 lfo2_internal_depth; /* 0-1200 */ guint16 lfo2_ctrl_depth; /* 0-1200 */ /* LFO3 - Pitch LFO */ guint32 lfo3_freq; /* pitch cents */ guint16 lfo3_internal_depth; /* cents */ guint16 lfo3_ctrl_depth; /* cents */ guint8 lfo3_ctrl; /* bit 6: LFO3 synch bit 8:invert attentuation ctrl */ /* filter parameters */ guint8 filter_type; /* 0=lowpass, 1=bandpass, 2=highpass, 3=bandreject */ guint8 turbo_lowpass; /* bit 7=0: on */ guint8 filter_cutoff; /* bit 8=1:on */ guint8 filter_midi_ctrl; /* bit 8=1:use ctrl rest 0:aftertouch */ guint8 filter_vel_scale; guint8 filter_resonance; /* bit 8=0:dynamic */ guint8 filter_breakpoint; /* bit 8=1:keyboard tracking */ /* velocity parameters */ guint8 vel_response; /* 0-4 = nonlinear, 5-9 = linear, 10-14 = special */ guint8 vel_dyn_range; /* release velocity paramaters */ guint8 release_vel_response; /* 0-4 = nonlinear, 5-9 = linear, 10-14 = special */ /* release velocity dynamic range? */ guint8 release_trigger_decay; guint8 attn_ctrl; /* bit 1:on, rest=ctrl, 0xFF=velocity */ guint8 max_velocity; /* Used for velocity split */ guint16 sample_offset; guint8 pitch_track_dim_bypass; /* bit 0=0: pitch track */ /* 0x10/0x20=dim bypass ctrl 94/95 */ guint8 layer_pan; /* 7-bit signed */ guint8 self_mask; /* 1=true */ guint8 channel_offset; /* (*4) */ guint8 sust_defeat; /* 2=on */ }; typedef enum { IPATCH_GIG_FILTER_LOWPASS = 0, IPATCH_GIG_FILTER_BANDPASS = 1, IPATCH_GIG_FILTER_HIGHPASS = 2, IPATCH_GIG_FILTER_BANDREJECT = 3 } IpatchGigFilterType; /* MIDI controllers used in GigaSampler files */ typedef enum { IPATCH_GIG_CTRL_MOD_WHEEL = 0x01, IPATCH_GIG_CTRL_BREATH = 0x02, IPATCH_GIG_CTRL_FOOT = 0x04, IPATCH_GIG_CTRL_PORTAMENTO_TIME = 0x05, IPATCH_GIG_CTRL_EFFECT_1 = 0x0C, IPATCH_GIG_CTRL_EFFECT_2 = 0x0D, IPATCH_GIG_CTRL_GEN_PURPOSE_1 = 0x10, IPATCH_GIG_CTRL_GEN_PURPOSE_2 = 0x11, IPATCH_GIG_CTRL_GEN_PURPOSE_3 = 0x12, IPATCH_GIG_CTRL_GEN_PURPOSE_4 = 0x13, IPATCH_GIG_CTRL_SUSTAIN_PEDAL = 0x40, IPATCH_GIG_CTRL_PORTAMENTO = 0x41, IPATCH_GIG_CTRL_SOSTENUTO = 0x42, IPATCH_GIG_CTRL_SOFT_PEDAL = 0x43, IPATCH_GIG_CTRL_GEN_PURPOSE_5 = 0x50, IPATCH_GIG_CTRL_GEN_PURPOSE_6 = 0x51, IPATCH_GIG_CTRL_GEN_PURPOSE_7 = 0x52, IPATCH_GIG_CTRL_GEN_PURPOSE_8 = 0x53, IPATCH_GIG_CTRL_EFFECT_DEPTH_1 = 0x5B, IPATCH_GIG_CTRL_EFFECT_DEPTH_2 = 0x5C, IPATCH_GIG_CTRL_EFFECT_DEPTH_3 = 0x5D, IPATCH_GIG_CTRL_EFFECT_DEPTH_4 = 0x5E, IPATCH_GIG_CTRL_EFFECT_DEPTH_5 = 0x5F } IpatchGigControlType; void ipatch_gig_parse_effects(IpatchFileHandle *handle, IpatchGigEffects *effects); void ipatch_gig_store_effects(IpatchFileHandle *handle, IpatchGigEffects *effects); void ipatch_gig_effects_init(IpatchGigEffects *effects); void ipatch_gig_effects_to_gen_array(IpatchGigEffects *effects, IpatchSF2GenArray *array); guint16 ipatch_gig_to_sf2_timecents(gint32 gig_tc); guint16 ipatch_gig_volsust_to_sf2_centibels(guint gig_tperc); #endif libinstpatch-1.1.6/libinstpatch/IpatchGigFile.c000066400000000000000000000062551400263525300215020ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchGigFile * @short_description: GigaSampler file object * @see_also: * @stability: Stable * * File type for GigaSampler files. */ #include #include #include #include #include "IpatchGigFile.h" #include "IpatchGigFile_priv.h" #include "IpatchDLSFile_priv.h" #include "i18n.h" #include "misc.h" static gboolean ipatch_gig_file_identify_method(IpatchFile *file, IpatchFileHandle *handle, GError **err); G_DEFINE_TYPE(IpatchGigFile, ipatch_gig_file, IPATCH_TYPE_DLS_FILE) /* GigaSampler file class init function */ static void ipatch_gig_file_class_init(IpatchGigFileClass *klass) { IpatchFileClass *file_class = IPATCH_FILE_CLASS(klass); file_class->identify = ipatch_gig_file_identify_method; /* load_object method handled by parent class IpatchDLSFile */ } static void ipatch_gig_file_init(IpatchGigFile *file) { } /* GigaSampler file identification method * ^&$#@*()!!!! They went and polluted the DLS file magic namespace. Rather * than check the file data, we use the primitive file extension. One does * not actually know if its a GigaSampler file until running into one of their * proprietary chunks (usually 3lnk in an instrument region). */ static gboolean ipatch_gig_file_identify_method(IpatchFile *file, IpatchFileHandle *handle, GError **err) { char *filename; guint32 buf[3]; gboolean retval = TRUE; int len; filename = ipatch_file_get_name(file); /* ++ alloc file name */ if(!filename) { return (FALSE); } len = strlen(filename); if(len < 4 || g_ascii_strcasecmp(filename + len - 4, ".gig") != 0) { retval = FALSE; } g_free(filename); /* -- free file name */ if(handle && retval) { /* Check for DLS signature */ if(!ipatch_file_read(handle, buf, 12, err) || buf[0] != IPATCH_FOURCC_RIFF || buf[2] != IPATCH_DLS_FOURCC_DLS) { retval = FALSE; } } return (retval); } /** * ipatch_gig_file_new: * * Create a new GigaSampler file object. * * Returns: New GigaSampler file object with a reference count of 1. * Caller owns the reference and removing it will destroy the item. */ IpatchGigFile * ipatch_gig_file_new(void) { return (IPATCH_GIG_FILE(g_object_new(IPATCH_TYPE_GIG_FILE, NULL))); } libinstpatch-1.1.6/libinstpatch/IpatchGigFile.h000066400000000000000000000040241400263525300214770ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_GIG_FILE_H__ #define __IPATCH_GIG_FILE_H__ #include #include #include #include typedef struct _IpatchGigFile IpatchGigFile; typedef struct _IpatchGigFileClass IpatchGigFileClass; #define IPATCH_TYPE_GIG_FILE (ipatch_gig_file_get_type ()) #define IPATCH_GIG_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_GIG_FILE, IpatchGigFile)) #define IPATCH_GIG_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_GIG_FILE, IpatchGigFileClass)) #define IPATCH_IS_GIG_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_GIG_FILE)) #define IPATCH_IS_GIG_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_GIG_FILE)) #define IPATCH_GIG_FILE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_GIG_FILE, \ IpatchGigFileClass)) /* GigaSampler file object (derived from IpatchDLSFile) */ struct _IpatchGigFile { IpatchDLSFile parent_instance; }; /* GigaSampler file class (derived from IpatchDLSFile) */ struct _IpatchGigFileClass { IpatchDLSFileClass parent_class; }; GType ipatch_gig_file_get_type(void); IpatchGigFile *ipatch_gig_file_new(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchGigFile_priv.h000066400000000000000000000110461400263525300225410ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_GIG_FILE_PRIV_H__ #define __IPATCH_GIG_FILE_PRIV_H__ /* * A GigaSampler file is based on DLS2 with many proprietary extensions. * Descriptions of chunks below that start with DLS are part of the DLS * standard, while the "Gig" ones are GigaSampler specific. * Extensions and quirks for the GigaSampler format: * * Toplevel file chunk is rather specific. * Sub chunks are listed in this order: * IARL IART ICMS ICMT ICOP ICRD IENG IGNR IKEY IMED INAM IPRD ISBJ ISFT * ISRC ISRF ITCH * The IARL chunk is always 256 bytes long and padded with spaces ' ' ???? * The ICMT chunk is 1024 bytes and padded with NULLs * All other chunks are 128 bytes and padded with NULLs * * * lins->ins: DLS instrument * INFO - DLS INFO LIST * INAM - Name always 64 bytes "GigaSampler Instrument Editor 2.0", etc * ISFT - Software always 12 bytes "Endless Wave" * dlid - DLS unique ID * insh - DLS instrument header * lrgn - DLS Region LIST * rgn - DLS instrument region (LIST) * rgnh - DLS region header * wsmp - DLS sample parameters * wlnk - DLS wave link parameters * 3lnk - Gig dimension info * 3prg - Gig LIST chunk * 3ewl - Gig LIST chunk (one for each sub region) * wsmp - DLS sample parameters (tuning, gain and loop) * 3ewa - Gig Envelop/LFO/Filter parameters (IpatchGigEffects) * 3ewl * wsmp * 3ewa * ... * 3dnl - Gig dimension names (up to 5 zero terminated strings) * 3ddp - Gig ???? (size 10, 2 byte words for each dimension?) * rgn - next DLS region * ... * lart - DLS Articulation LIST * 3ewg - Gig global instrument parameters * 3gri - Gig LIST * 3gnl - Gig LIST * 3gnm - Gig sample group names * ptbl - DLS pool table * wvpl - DLS wave pool LIST * wave - DLS RIFF wave file * fmt - DLS WAVE format * INFO - DLS INFO list * INAM - Name always 64 bytes * data - DLS WAVE sample data * smpl - Gig sample parameters * 3gix - Gig sample group number * einf - Unknown (perhaps to speed up loading?) */ /* RIFF chunk FOURCC guint32 integers - list chunks*/ #define IPATCH_GIG_FOURCC_3PRG IPATCH_FOURCC ('3','p','r','g') #define IPATCH_GIG_FOURCC_3EWL IPATCH_FOURCC ('3','e','w','l') #define IPATCH_GIG_FOURCC_3DNL IPATCH_FOURCC ('3','d','n','l') #define IPATCH_GIG_FOURCC_3GNL IPATCH_FOURCC ('3','g','n','l') #define IPATCH_GIG_FOURCC_3GRI IPATCH_FOURCC ('3','g','r','i') /* sub chunks */ #define IPATCH_GIG_FOURCC_SMPL IPATCH_FOURCC ('s','m','p','l') #define IPATCH_GIG_FOURCC_3DDP IPATCH_FOURCC ('3','d','d','p') #define IPATCH_GIG_FOURCC_3EWA IPATCH_FOURCC ('3','e','w','a') #define IPATCH_GIG_FOURCC_3EWG IPATCH_FOURCC ('3','e','w','g') #define IPATCH_GIG_FOURCC_3GIX IPATCH_FOURCC ('3','g','i','x') #define IPATCH_GIG_FOURCC_3GNM IPATCH_FOURCC ('3','g','n','m') #define IPATCH_GIG_FOURCC_3LNK IPATCH_FOURCC ('3','l','n','k') #define IPATCH_GIG_FOURCC_EINF IPATCH_FOURCC ('e','i','n','f') /* file chunk sizes */ #define IPATCH_GIG_SMPL_SIZE 60 #define IPATCH_GIG_3DDP_SIZE 10 #define IPATCH_GIG_3EWA_SIZE 140 #define IPATCH_GIG_3EWG_SIZE 12 #define IPATCH_GIG_3GIX_SIZE 4 #define IPATCH_GIG_3GNM_SIZE 64 #define IPATCH_GIG_3LNK_SIZE 172 /* size of instrument and sample name INFO chunk sizes */ #define IPATCH_GIG_ITEM_INAM_SIZE 64 /* fixed sizes for toplevel file INFO chunks */ #define IPATCH_GIG_MOST_INFO_SIZE 128 /* size of all chunks except 2 below */ #define IPATCH_GIG_IARL_INFO_SIZE 256 /* this one is padded with spaces ' ' */ #define IPATCH_GIG_ICMT_INFO_SIZE 1024 /* Software INFO value for GigaSampler instruments */ /* FIXME - Should we put something else there? */ #define IPATCH_GIG_INST_ISFT_VAL "Endless Wave" #endif libinstpatch-1.1.6/libinstpatch/IpatchGigInst.c000066400000000000000000000167571400263525300215500ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchGigInst * @short_description: GigaSampler instrument object * @see_also: * @stability: Stable * * GigaSampler instrument objects are the toplevel instrument objects in a * GigaSampler file. */ #include #include #include #include #include "IpatchGigInst.h" #include "IpatchGigRegion.h" #include "IpatchGigFile.h" #include "IpatchGigFile_priv.h" #include "i18n.h" enum { PROP_0 }; static void ipatch_gig_inst_class_init(IpatchGigInstClass *klass); static void ipatch_gig_inst_init(IpatchGigInst *inst); static void ipatch_gig_inst_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_gig_inst_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_gig_inst_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static const GType *ipatch_gig_inst_container_child_types(void); static gboolean ipatch_gig_inst_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type); static gpointer parent_class = NULL; static GType inst_child_types[2] = { 0 }; GType ipatch_gig_inst_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchGigInstClass), NULL, NULL, (GClassInitFunc)ipatch_gig_inst_class_init, NULL, NULL, sizeof(IpatchGigInst), 0, (GInstanceInitFunc)ipatch_gig_inst_init, }; item_type = g_type_register_static(IPATCH_TYPE_DLS2_INST, "IpatchGigInst", &item_info, 0); } return (item_type); } static void ipatch_gig_inst_class_init(IpatchGigInstClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->get_property = ipatch_gig_inst_get_property; /* we use the IpatchItem item_set_property method */ item_class->item_set_property = ipatch_gig_inst_set_property; item_class->copy = ipatch_gig_inst_item_copy; container_class->child_types = ipatch_gig_inst_container_child_types; container_class->init_iter = ipatch_gig_inst_container_init_iter; inst_child_types[0] = IPATCH_TYPE_GIG_REGION; } static void ipatch_gig_inst_init(IpatchGigInst *inst) { guint8 def_3ewg[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x38, 0x23 }; /* FIXME - Proper default values? */ inst->attenuate = 0; inst->effect_send = 0; inst->fine_tune = 0; inst->pitch_bend_range = 2; /* 2 semitones */ inst->dim_key_start = 0; inst->dim_key_end = 0; /* FIXME - What is it for? */ memcpy(inst->chunk_3ewg, def_3ewg, IPATCH_GIG_3EWG_SIZE); } static void ipatch_gig_inst_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch(property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_gig_inst_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch(property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_gig_inst_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchGigInst *src_inst, *dest_inst; src_inst = IPATCH_GIG_INST(src); dest_inst = IPATCH_GIG_INST(dest); /* call IpatchDLS2Inst class copy function */ IPATCH_ITEM_CLASS(parent_class)->copy(dest, src, link_func, user_data); /* don't need to lock for this stuff */ dest_inst->attenuate = src_inst->attenuate; dest_inst->effect_send = src_inst->effect_send; dest_inst->fine_tune = src_inst->fine_tune; dest_inst->pitch_bend_range = src_inst->pitch_bend_range; dest_inst->dim_key_start = src_inst->dim_key_start; dest_inst->dim_key_end = src_inst->dim_key_end; IPATCH_ITEM_RLOCK(src_inst); memcpy(dest_inst->chunk_3ewg, src_inst->chunk_3ewg, IPATCH_GIG_3EWG_SIZE); IPATCH_ITEM_RUNLOCK(src_inst); } static const GType * ipatch_gig_inst_container_child_types(void) { return (inst_child_types); } /* container is locked by caller */ static gboolean ipatch_gig_inst_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type) { IpatchGigInst *inst = IPATCH_GIG_INST(container); if(!g_type_is_a(type, IPATCH_TYPE_GIG_REGION)) { g_critical("Invalid child type '%s' for parent of type '%s'", g_type_name(type), g_type_name(G_OBJECT_TYPE(container))); return (FALSE); } ipatch_iter_GSList_init(iter, &((IpatchDLS2Inst *)inst)->regions); return (TRUE); } /** * ipatch_gig_inst_new: * * Create a new GigaSampler instrument object. * * Returns: New GigaSampler instrument with a reference count of 1. Caller * owns the reference and removing it will destroy the item, unless another * reference is added (if its parented for example). */ IpatchGigInst * ipatch_gig_inst_new(void) { return (IPATCH_GIG_INST(g_object_new(IPATCH_TYPE_GIG_INST, NULL))); } /** * ipatch_gig_inst_first: (skip) * @iter: Patch item iterator containing #IpatchGigInst items * * Gets the first item in a GigaSampler instrument iterator. A convenience * wrapper for ipatch_iter_first(). * * Returns: The first GigaSampler instrument in @iter or %NULL if empty. */ IpatchGigInst * ipatch_gig_inst_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_GIG_INST(obj)); } else { return (NULL); } } /** * ipatch_gig_inst_next: (skip) * @iter: Patch item iterator containing #IpatchGigInst items * * Gets the next item in a GigaSampler instrument iterator. A convenience * wrapper for ipatch_iter_next(). * * Returns: The next GigaSampler instrument in @iter or %NULL if at * the end of the list. */ IpatchGigInst * ipatch_gig_inst_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_GIG_INST(obj)); } else { return (NULL); } } libinstpatch-1.1.6/libinstpatch/IpatchGigInst.h000066400000000000000000000045461400263525300215460ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_GIG_INST_H__ #define __IPATCH_GIG_INST_H__ #include #include /* forward type declarations */ typedef struct _IpatchGigInst IpatchGigInst; typedef struct _IpatchGigInstClass IpatchGigInstClass; typedef struct _IpatchGigInstParams IpatchGigInstParams; #include #define IPATCH_TYPE_GIG_INST (ipatch_gig_inst_get_type ()) #define IPATCH_GIG_INST(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_GIG_INST, \ IpatchGigInst)) #define IPATCH_GIG_INST_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_GIG_INST, \ IpatchGigInstClass)) #define IPATCH_IS_GIG_INST(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_GIG_INST)) #define IPATCH_IS_GIG_INST_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_GIG_INST)) #define IPATCH_GIG_INST_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_GIG_INST, \ IpatchGigInstClass)) /* GigaSampler instrument object */ struct _IpatchGigInst { IpatchDLS2Inst parent_instance; guint32 attenuate; guint16 effect_send; guint16 fine_tune; guint16 pitch_bend_range; guint8 dim_key_start; /* bit 1: piano release */ guint8 dim_key_end; guint8 chunk_3ewg[12]; /* 3ewg chunk - FIXME what is it? */ }; struct _IpatchGigInstClass { IpatchDLS2InstClass parent_class; }; GType ipatch_gig_inst_get_type(void); IpatchGigInst *ipatch_gig_inst_new(void); IpatchGigInst *ipatch_gig_inst_first(IpatchIter *iter); IpatchGigInst *ipatch_gig_inst_next(IpatchIter *iter); #endif libinstpatch-1.1.6/libinstpatch/IpatchGigRegion.c000066400000000000000000000621771400263525300220530ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchGigRegion * @short_description: GigaSampler region object * @see_also: #IpatchGigInst, #IpatchGig * @stability: Stable * * GigaSampler region objects are children of #IpatchGigInst objects. */ #include #include #include "IpatchGigRegion.h" #include "IpatchGigFile.h" #include "IpatchGigFile_priv.h" #include "IpatchGigSample.h" #include "IpatchRange.h" #include "IpatchTypeProp.h" #include "ipatch_priv.h" enum { PROP_0, PROP_TITLE, PROP_NOTE_RANGE, PROP_VELOCITY_RANGE, PROP_KEY_GROUP, PROP_LAYER_GROUP, PROP_PHASE_GROUP, PROP_CHANNEL, /* IpatchItem flags (no one needs to know that though) */ PROP_SELF_NON_EXCLUSIVE, PROP_PHASE_MASTER, PROP_MULTI_CHANNEL }; static void ipatch_gig_region_class_init(IpatchGigRegionClass *klass); static void ipatch_gig_region_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_gig_region_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_gig_region_init(IpatchGigRegion *gig_region); static void ipatch_gig_region_finalize(GObject *gobject); static void ipatch_gig_region_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static const GType *ipatch_gig_region_container_child_types(void); static gboolean ipatch_gig_region_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type); static GObjectClass *parent_class = NULL; static GType gig_region_child_types[3] = { 0 }; GType ipatch_gig_region_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchGigRegionClass), NULL, NULL, (GClassInitFunc)ipatch_gig_region_class_init, NULL, NULL, sizeof(IpatchGigRegion), 0, (GInstanceInitFunc)ipatch_gig_region_init, }; item_type = g_type_register_static(IPATCH_TYPE_CONTAINER, "IpatchGigRegion", &item_info, 0); } return (item_type); } static void ipatch_gig_region_class_init(IpatchGigRegionClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->finalize = ipatch_gig_region_finalize; obj_class->get_property = ipatch_gig_region_get_property; item_class->item_set_property = ipatch_gig_region_set_property; item_class->copy = ipatch_gig_region_item_copy; container_class->child_types = ipatch_gig_region_container_child_types; container_class->init_iter = ipatch_gig_region_container_init_iter; container_class->make_unique = NULL; g_object_class_override_property(obj_class, PROP_TITLE, "title"); g_object_class_install_property(obj_class, PROP_NOTE_RANGE, ipatch_param_spec_range("note-range", _("Note range"), _("MIDI note range"), 0, 127, 0, 127, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_VELOCITY_RANGE, ipatch_param_spec_range("velocity-range", _("Velocity range"), _("MIDI velocity range"), 0, 127, 0, 127, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_KEY_GROUP, g_param_spec_int("key-group", _("Key group"), _("Percussion key group"), 0, 15, 0, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_LAYER_GROUP, g_param_spec_int("layer-group", _("Layer group"), _("Layer group"), 0, G_MAXUSHORT, 0, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_PHASE_GROUP, g_param_spec_int("phase-group", _("Phase group"), _("Phase locked sample group"), 0, G_MAXUSHORT, 0, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_CHANNEL, g_param_spec_int("channel", _("Channel"), _("DLS audio channel identifier"), 0, 0x03FFFF, 0, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_SELF_NON_EXCLUSIVE, g_param_spec_boolean("self-non-exclusive", _("Non exclusive"), _("Self non exclusive"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_PHASE_MASTER, g_param_spec_boolean("phase-master", _("Phase master"), _("Multi channel phase lock master"), FALSE, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_MULTI_CHANNEL, g_param_spec_boolean("multi-channel", _("Multi channel"), _("Multi channel"), FALSE, G_PARAM_READWRITE)); gig_region_child_types[0] = IPATCH_TYPE_GIG_DIMENSION; gig_region_child_types[1] = IPATCH_TYPE_GIG_SUB_REGION; } static void ipatch_gig_region_get_title(IpatchGigRegion *region, GValue *value) { IpatchRange *range = NULL; char *s = NULL; g_object_get(region, "note-range", &range, NULL); if(range) { if(range->low != range->high) { s = g_strdup_printf(_("Note %d-%d"), range->low, range->high); } else { s = g_strdup_printf(_("Note %d"), range->low); } ipatch_range_free(range); } g_value_take_string(value, s); } static void ipatch_gig_region_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchGigRegion *region = IPATCH_GIG_REGION(object); IpatchRange *range; gboolean retval; switch(property_id) { case PROP_NOTE_RANGE: range = ipatch_value_get_range(value); if(range) { ipatch_gig_region_set_note_range(region, range->low, range->high); } break; case PROP_VELOCITY_RANGE: range = ipatch_value_get_range(value); if(range) { IPATCH_ITEM_WLOCK(region); region->velocity_range_low = range->low; region->velocity_range_high = range->high; IPATCH_ITEM_WUNLOCK(region); } break; case PROP_KEY_GROUP: region->key_group = g_value_get_int(value); break; case PROP_LAYER_GROUP: region->layer_group = g_value_get_int(value); break; case PROP_PHASE_GROUP: region->phase_group = g_value_get_int(value); break; case PROP_CHANNEL: region->channel = g_value_get_int(value); break; case PROP_SELF_NON_EXCLUSIVE: if(g_value_get_boolean(value)) ipatch_item_set_flags(IPATCH_ITEM(object), IPATCH_GIG_REGION_SELF_NON_EXCLUSIVE); else ipatch_item_clear_flags(IPATCH_ITEM(object), IPATCH_GIG_REGION_SELF_NON_EXCLUSIVE); break; case PROP_PHASE_MASTER: if(g_value_get_boolean(value)) ipatch_item_set_flags(IPATCH_ITEM(object), IPATCH_GIG_REGION_PHASE_MASTER); else ipatch_item_clear_flags(IPATCH_ITEM(object), IPATCH_GIG_REGION_PHASE_MASTER); break; case PROP_MULTI_CHANNEL: if(g_value_get_boolean(value)) ipatch_item_set_flags(IPATCH_ITEM(object), IPATCH_GIG_REGION_MULTI_CHANNEL); else ipatch_item_clear_flags(IPATCH_ITEM(object), IPATCH_GIG_REGION_MULTI_CHANNEL); break; default: IPATCH_ITEM_WLOCK(region); retval = ipatch_dls2_info_set_property(®ion->info, property_id, value); IPATCH_ITEM_WUNLOCK(region); if(!retval) { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } break; } } static void ipatch_gig_region_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchGigRegion *region = IPATCH_GIG_REGION(object); IpatchRange range; gboolean bool, retval; switch(property_id) { case PROP_TITLE: ipatch_gig_region_get_title(region, value); break; case PROP_NOTE_RANGE: IPATCH_ITEM_RLOCK(region); range.low = region->note_range_low; range.high = region->note_range_high; IPATCH_ITEM_RUNLOCK(region); ipatch_value_set_range(value, &range); break; case PROP_VELOCITY_RANGE: IPATCH_ITEM_RLOCK(region); range.low = region->velocity_range_low; range.high = region->velocity_range_high; IPATCH_ITEM_RUNLOCK(region); ipatch_value_set_range(value, &range); break; case PROP_KEY_GROUP: g_value_set_int(value, region->key_group); break; case PROP_LAYER_GROUP: g_value_set_int(value, region->layer_group); break; case PROP_PHASE_GROUP: g_value_set_int(value, region->phase_group); break; case PROP_CHANNEL: g_value_set_int(value, region->channel); break; case PROP_SELF_NON_EXCLUSIVE: bool = (ipatch_item_get_flags(IPATCH_ITEM(object)) & IPATCH_GIG_REGION_SELF_NON_EXCLUSIVE) > 0; g_value_set_boolean(value, bool); break; case PROP_PHASE_MASTER: bool = (ipatch_item_get_flags(IPATCH_ITEM(object)) & IPATCH_GIG_REGION_PHASE_MASTER) > 0; g_value_set_boolean(value, bool); break; case PROP_MULTI_CHANNEL: bool = (ipatch_item_get_flags(IPATCH_ITEM(object)) & IPATCH_GIG_REGION_MULTI_CHANNEL) > 0; g_value_set_boolean(value, bool); break; default: IPATCH_ITEM_RLOCK(region); retval = ipatch_dls2_info_get_property(region->info, property_id, value); IPATCH_ITEM_RUNLOCK(region); if(!retval) { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } break; } } static void ipatch_gig_region_init(IpatchGigRegion *region) { int i; region->note_range_low = 0; region->note_range_high = 127; region->velocity_range_low = 0; region->velocity_range_high = 127; region->key_group = 0; region->layer_group = 0; region->phase_group = 0; region->channel = 0; region->info = NULL; region->dimension_count = 0; region->sub_region_count = 1; /* always at least 1 sub region */ region->sub_regions[0] = ipatch_gig_sub_region_new(); ipatch_item_set_parent(IPATCH_ITEM(region->sub_regions[0]), IPATCH_ITEM(region)); /* FIXME - What is this for really? */ for(i = 0; i < IPATCH_GIG_3DDP_SIZE; i++) { region->chunk_3ddp[i] = 0xFF; } } static void ipatch_gig_region_finalize(GObject *gobject) { IpatchGigRegion *gig_region = IPATCH_GIG_REGION(gobject); int i; IPATCH_ITEM_WLOCK(gig_region); /* delete all dimensions */ for(i = 0; i < gig_region->dimension_count; i++) { g_object_unref(gig_region->dimensions[i]); gig_region->dimensions[i] = NULL; } /* delete all sub regions */ for(i = 0; i < gig_region->sub_region_count; i++) { g_object_unref(gig_region->sub_regions[i]); gig_region->sub_regions[i] = NULL; } IPATCH_ITEM_WUNLOCK(gig_region); if(parent_class->finalize) { parent_class->finalize(gobject); } } static void ipatch_gig_region_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchGigRegion *src_reg, *dest_reg; IpatchGigDimension *src_dim; IpatchGigSubRegion *src_sub; IpatchItem *new_dim, *new_sub; int i; src_reg = IPATCH_GIG_REGION(src); dest_reg = IPATCH_GIG_REGION(dest); IPATCH_ITEM_RLOCK(src_reg); /* duplicate the flags */ ipatch_item_set_flags(dest_reg, ipatch_item_get_flags(src_reg) & IPATCH_GIG_REGION_FLAG_MASK); dest_reg->note_range_low = src_reg->note_range_low; dest_reg->note_range_high = src_reg->note_range_high; dest_reg->velocity_range_low = src_reg->velocity_range_low; dest_reg->velocity_range_high = src_reg->velocity_range_high; dest_reg->key_group = src_reg->key_group; dest_reg->layer_group = src_reg->layer_group; dest_reg->phase_group = src_reg->phase_group; dest_reg->channel = src_reg->channel; dest_reg->info = ipatch_dls2_info_duplicate(src_reg->info); /* copy dimensions */ for(i = 0; i < src_reg->dimension_count; i++) { src_dim = src_reg->dimensions[i]; new_dim = ipatch_item_duplicate_link_func((IpatchItem *)src_dim, link_func, user_data); g_return_if_fail(new_dim != NULL); dest_reg->dimensions[i] = IPATCH_GIG_DIMENSION(new_dim); dest_reg->dimension_count = i + 1; /* update count in case of failure */ } /* copy sub regions */ for(i = 0; i < src_reg->sub_region_count; i++) { src_sub = src_reg->sub_regions[i]; new_sub = ipatch_item_duplicate_link_func((IpatchItem *)src_sub, link_func, user_data); g_return_if_fail(new_sub != NULL); dest_reg->sub_regions[i] = IPATCH_GIG_SUB_REGION(new_sub); dest_reg->sub_region_count = i + 1; /* update count in case of failure */ } IPATCH_ITEM_RUNLOCK(src_reg); } static const GType * ipatch_gig_region_container_child_types(void) { return (gig_region_child_types); } /* container is locked by caller */ static gboolean ipatch_gig_region_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type) { IpatchGigRegion *region = IPATCH_GIG_REGION(container); if(g_type_is_a(type, IPATCH_TYPE_GIG_DIMENSION)) ipatch_iter_array_init(iter, (gpointer *)(region->dimensions), region->dimension_count); else if(g_type_is_a(type, IPATCH_TYPE_GIG_SUB_REGION)) ipatch_iter_array_init(iter, (gpointer *)(region->sub_regions), region->sub_region_count); else { g_critical("Invalid child type '%s' for parent of type '%s'", g_type_name(type), g_type_name(G_OBJECT_TYPE(container))); return (FALSE); } return (TRUE); } /** * ipatch_gig_region_new: * * Create a new GigaSampler instrument region. * * Returns: New GigaSampler region with a ref count of 1 which the caller * owns. */ IpatchGigRegion * ipatch_gig_region_new(void) { return (IPATCH_GIG_REGION(g_object_new(IPATCH_TYPE_GIG_REGION, NULL))); } /** * ipatch_gig_region_first: (skip) * @iter: Patch item iterator containing #IpatchGigRegion items * * Gets the first item in a region iterator. A convenience * wrapper for ipatch_iter_first(). * * Returns: The first region in @iter or %NULL if empty. */ IpatchGigRegion * ipatch_gig_region_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_GIG_REGION(obj)); } else { return (NULL); } } /** * ipatch_gig_region_next: (skip) * @iter: Patch item iterator containing #IpatchGigRegion items * * Gets the next item in a region iterator. A convenience wrapper * for ipatch_iter_next(). * * Returns: The next region in @iter or %NULL if at the end of * the list. */ IpatchGigRegion * ipatch_gig_region_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_GIG_REGION(obj)); } else { return (NULL); } } /** * ipatch_gig_region_set_note_range: * @region: Region to set note range of * @low: Low value of range (MIDI note # between 0 and 127) * @high: High value of range (MIDI note # between 0 and 127) * * Set the MIDI note range that a region is active on. */ void ipatch_gig_region_set_note_range(IpatchGigRegion *region, int low, int high) { GValue titleval = { 0 }; g_return_if_fail(IPATCH_IS_GIG_REGION(region)); g_return_if_fail(low >= 0 && low <= 127); g_return_if_fail(high >= 0 && high <= 127); if(low > high) /* swap if backwards */ { int temp = low; low = high; high = temp; } IPATCH_ITEM_WLOCK(region); region->note_range_low = low; region->note_range_high = high; IPATCH_ITEM_WUNLOCK(region); /* title property notify */ g_value_init(&titleval, G_TYPE_STRING); ipatch_gig_region_get_title(region, &titleval); ipatch_item_prop_notify((IpatchItem *)region, ipatch_item_pspec_title, &titleval, NULL); g_value_unset(&titleval); } /** * ipatch_gig_region_set_velocity_range: * @region: Region to set velocity range of * @low: Low value of range (MIDI velocity # between 0 and 127) * @high: High value of range (MIDI velocity # between 0 and 127) * * Set the MIDI velocity range that a region is active on. */ void ipatch_gig_region_set_velocity_range(IpatchGigRegion *region, int low, int high) { g_return_if_fail(IPATCH_IS_GIG_REGION(region)); g_return_if_fail(low >= 0 && low <= 127); g_return_if_fail(high >= 0 && high <= 127); if(low > high) /* swap if backwards */ { int temp = low; low = high; high = temp; } IPATCH_ITEM_WLOCK(region); region->velocity_range_low = low; region->velocity_range_high = high; IPATCH_ITEM_WUNLOCK(region); } /** * ipatch_gig_region_new_dimension: * @region: GigaSampler region * @type: Type of dimension to add * @split_count: Split bit count * * Adds a new dimension to a GigaSampler region. The dimension is allocated * @split_count number of dimension bits which means the total number of * sub regions will be multiplied by a factor of 2 to the power of * @split_count. There can be a maximum of 32 sub regions for a total of * 5 used split bits. */ void ipatch_gig_region_new_dimension(IpatchGigRegion *region, IpatchGigDimensionType type, int split_count) { IpatchGigDimension *dimension; int new_sub_region_count; int mask, shift; int i; g_return_if_fail(IPATCH_IS_GIG_REGION(region)); g_return_if_fail(split_count > 0); IPATCH_ITEM_WLOCK(region); new_sub_region_count = region->sub_region_count * (1 << split_count); if(log_if_fail(new_sub_region_count <= 32)) { IPATCH_ITEM_WUNLOCK(region); return; } /* calculate dimension split bit shift value */ for(i = region->sub_region_count, shift = 0; !(i & 1); i >>= 1, shift++); /* calculate unshifted mask for the split bit count */ for(i = 0, mask = 0; i < split_count; i++, mask = (mask << 1) | 1); dimension = ipatch_gig_dimension_new(); dimension->type = type; dimension->split_count = split_count; dimension->split_mask = mask << shift; dimension->split_shift = shift; region->dimensions[region->dimension_count] = dimension; region->dimension_count++; ipatch_item_set_parent(IPATCH_ITEM(dimension), IPATCH_ITEM(region)); for(i = region->sub_region_count; i < new_sub_region_count; i++) { region->sub_regions[i] = ipatch_gig_sub_region_new(); ipatch_item_set_parent(IPATCH_ITEM(region->sub_regions[i]), IPATCH_ITEM(region)); } region->sub_region_count = new_sub_region_count; IPATCH_ITEM_WUNLOCK(region); } /** * ipatch_gig_region_remove_dimension: * @region: GigaSampler region * @dim_index: Index of an existing dimension to remove (0-4) * @split_index: Split index to use in the dimension to remove * * Removes a dimension from a GigaSampler region, including all related * sub regions (except those that correspond to the @split_index), and * re-organizes sub regions for new dimension map. */ void ipatch_gig_region_remove_dimension(IpatchGigRegion *region, int dim_index, int split_index) { IpatchGigSubRegion *new_regions[32] = { NULL }; int new_region_index = 0; guint max_split_index; guint8 index[5]; /* current index for each dimension */ guint8 max[5]; /* max count for each dimension (+1) */ int sub_index, bit_index; int i; g_return_if_fail(IPATCH_IS_GIG_REGION(region)); IPATCH_ITEM_WLOCK(region); if(log_if_fail(dim_index >= 0 && dim_index < region->dimension_count)) { IPATCH_ITEM_WUNLOCK(region); return; } max_split_index = 1 << region->dimensions[dim_index]->split_count; if(log_if_fail(split_index > 0 && (guint)split_index < max_split_index)) { IPATCH_ITEM_WUNLOCK(region); return; } /* initialize dimension index and max arrays */ for(i = 0; i < region->dimension_count; i++) { index[i] = 0; max[i] = 1 << region->dimensions[i]->split_count; } index[dim_index] = split_index; /* the split index to use */ /* move pointers of sub regions we want to keep to new_regions array */ while(TRUE) { /* calculate current sub region index */ sub_index = 0; bit_index = 0; for(i = 0; i < region->dimension_count; i++) { sub_index += index[i] << bit_index; bit_index += region->dimensions[i]->split_count; } /* move the sub region pointer to new region array */ new_regions[new_region_index++] = region->sub_regions[sub_index]; region->sub_regions[sub_index] = NULL; /* clear pointer */ /* increment dimension indexes in binary fashion */ i = (dim_index != 0) ? 0 : 1; /* first dimension to increment index of */ while(i < region->dimension_count) { if(++index[i] < max[i]) { break; /* carry bit to next dimension? */ } index[i] = 0; if(++i == dim_index) { i++; /* skip remove dimension */ } } if(i == region->dimension_count) { break; /* are we done yet? */ } } /* free unused sub regions */ for(i = 0; i < region->sub_region_count; i++) if(region->sub_regions[i]) { g_object_unref(region->sub_regions[i]); } /* copy saved sub region pointers back into region */ for(i = 0; i < new_region_index; i++) { region->sub_regions[i] = new_regions[i]; } /* shift dimensions down into the deleted slot */ for(i = dim_index; i < region->dimension_count - 1; i++) { region->dimensions[i] = region->dimensions[i + 1]; } region->sub_region_count = new_region_index; region->dimension_count--; IPATCH_ITEM_WUNLOCK(region); } libinstpatch-1.1.6/libinstpatch/IpatchGigRegion.h000066400000000000000000000107501400263525300220460ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_GIG_REGION_H__ #define __IPATCH_GIG_REGION_H__ #include #include #include /* forward type declarations */ typedef struct _IpatchGigRegion IpatchGigRegion; typedef struct _IpatchGigRegionClass IpatchGigRegionClass; #include #include #include #include #include #define IPATCH_TYPE_GIG_REGION (ipatch_gig_region_get_type ()) #define IPATCH_GIG_REGION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_GIG_REGION, \ IpatchGigRegion)) #define IPATCH_GIG_REGION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_GIG_REGION, \ IpatchGigRegionClass)) #define IPATCH_IS_GIG_REGION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_GIG_REGION)) #define IPATCH_IS_GIG_REGION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_GIG_REGION)) #define IPATCH_GIG_REGION_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_GIG_REGION, \ IpatchGigRegionClass)) /* GigaSampler region object */ struct _IpatchGigRegion { IpatchContainer parent_instance; /*< private >*/ guint8 note_range_low; /* MIDI note range low value */ guint8 note_range_high; /* MIDI note range high value */ guint8 velocity_range_low; /* MIDI velocity range low value */ guint8 velocity_range_high; /* MIDI velocity range high value */ guint16 key_group; /* Exclusive key group number or 0 */ guint16 layer_group; /* layer group (descriptive only) */ guint16 phase_group; /* Phase locked group number or 0 */ guint16 channel; /* channel ID (IpatchDLS2RegionChannelType) */ IpatchDLS2Info *info; /* info string values */ guint8 dimension_count; /* dimension count (0-5) */ guint8 sub_region_count; /* 2 ^ sum (dimensions[].split_count) (1-32) */ IpatchGigDimension *dimensions[5]; /* [dimension_count] */ IpatchGigSubRegion *sub_regions[32]; /* [sub_region_count] */ guint8 chunk_3ddp[10]; /* FIXME - what is it? (16 bits / dimension?) */ }; /* GigaSampler region class */ struct _IpatchGigRegionClass { IpatchContainerClass parent_class; }; /* Flags crammed into IpatchItem flags (ditched 2 - 16 bit flag fields) */ /* FIXME - Are these used in GigaSampler files? */ typedef enum { IPATCH_GIG_REGION_SELF_NON_EXCLUSIVE = 1 << IPATCH_CONTAINER_UNUSED_FLAG_SHIFT, IPATCH_GIG_REGION_PHASE_MASTER = 1 << (IPATCH_CONTAINER_UNUSED_FLAG_SHIFT + 1), IPATCH_GIG_REGION_MULTI_CHANNEL = 1 << (IPATCH_CONTAINER_UNUSED_FLAG_SHIFT + 2) } IpatchGigRegionFlags; /** * IPATCH_GIG_REGION_FLAG_MASK: (skip) */ #define IPATCH_GIG_REGION_FLAG_MASK (0x0F << IPATCH_CONTAINER_UNUSED_FLAG_SHIFT) /** * IPATCH_GIG_REGION_UNUSED_FLAG_SHIFT: (skip) */ /* 3 flags + 1 for expansion */ #define IPATCH_GIG_REGION_UNUSED_FLAG_SHIFT \ (IPATCH_CONTAINER_UNUSED_FLAG_SHIFT + 4) GType ipatch_gig_region_get_type(void); IpatchGigRegion *ipatch_gig_region_new(void); IpatchGigRegion *ipatch_gig_region_first(IpatchIter *iter); IpatchGigRegion *ipatch_gig_region_next(IpatchIter *iter); void ipatch_gig_region_set_note_range(IpatchGigRegion *region, int low, int high); void ipatch_gig_region_set_velocity_range(IpatchGigRegion *region, int low, int high); void ipatch_gig_region_new_dimension(IpatchGigRegion *region, IpatchGigDimensionType type, int split_count); void ipatch_gig_region_remove_dimension(IpatchGigRegion *region, int dim_index, int split_index); #endif libinstpatch-1.1.6/libinstpatch/IpatchGigSample.c000066400000000000000000000144041400263525300220370ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchGigSample * @short_description: GigaSampler sample object * @see_also: #IpatchGig * @stability: Stable * * Object defining a GigaSampler sample object. Child of #IpatchGig objects * and referenced by #IpatchGigSubRegion objects. */ #include #include #include #include "IpatchGigSample.h" #include "i18n.h" enum { PROP_0, PROP_GROUP_NUMBER }; static void ipatch_gig_sample_class_init(IpatchGigSampleClass *klass); static void ipatch_gig_sample_init(IpatchGigSample *sample); static void ipatch_gig_sample_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_gig_sample_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_gig_sample_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static gpointer parent_class = NULL; GType ipatch_gig_sample_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchGigSampleClass), NULL, NULL, (GClassInitFunc)ipatch_gig_sample_class_init, NULL, NULL, sizeof(IpatchGigSample), 0, (GInstanceInitFunc)ipatch_gig_sample_init, }; item_type = g_type_register_static(IPATCH_TYPE_DLS2_SAMPLE, "IpatchGigSample", &item_info, 0); } return (item_type); } static void ipatch_gig_sample_class_init(IpatchGigSampleClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->get_property = ipatch_gig_sample_get_property; /* we use the IpatchItem item_set_property method */ item_class->item_set_property = ipatch_gig_sample_set_property; item_class->copy = ipatch_gig_sample_item_copy; g_object_class_install_property(obj_class, PROP_GROUP_NUMBER, g_param_spec_uint("group-number", _("Group number"), _("Sample group index"), 0, G_MAXUINT, 0, G_PARAM_READWRITE)); } static void ipatch_gig_sample_init(IpatchGigSample *sample) { sample->group_number = 0; } static void ipatch_gig_sample_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchGigSample *sample = IPATCH_GIG_SAMPLE(object); switch(property_id) { case PROP_GROUP_NUMBER: sample->group_number = g_value_get_uint(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_gig_sample_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchGigSample *sample = IPATCH_GIG_SAMPLE(object); switch(property_id) { case PROP_GROUP_NUMBER: g_value_set_uint(value, sample->group_number); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_gig_sample_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchGigSample *src_sample, *dest_sample; src_sample = IPATCH_GIG_SAMPLE(src); dest_sample = IPATCH_GIG_SAMPLE(dest); /* call IpatchDLS2Sample class copy function */ IPATCH_ITEM_CLASS(parent_class)->copy(dest, src, link_func, user_data); /* don't need to lock for this stuff */ dest_sample->group_number = src_sample->group_number; } /** * ipatch_gig_sample_new: * * Create a new GigaSampler sample object. * * Returns: New GigaSampler sample with a reference count of 1. Caller * owns the reference and removing it will destroy the item, unless another * reference is added (if its parented for example). */ IpatchGigSample * ipatch_gig_sample_new(void) { return (IPATCH_GIG_SAMPLE(g_object_new(IPATCH_TYPE_GIG_SAMPLE, NULL))); } /** * ipatch_gig_sample_first: (skip) * @iter: Patch item iterator containing #IpatchGigSample items * * Gets the first item in a GigaSampler sample iterator. A convenience * wrapper for ipatch_iter_first(). * * Returns: The first GigaSampler sample in @iter or %NULL if empty. */ IpatchGigSample * ipatch_gig_sample_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_GIG_SAMPLE(obj)); } else { return (NULL); } } /** * ipatch_gig_sample_next: (skip) * @iter: Patch item iterator containing #IpatchGigSample items * * Gets the next item in a GigaSampler sample iterator. A convenience * wrapper for ipatch_iter_next(). * * Returns: The next GigaSampler sample in @iter or %NULL if at * the end of the list. */ IpatchGigSample * ipatch_gig_sample_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_GIG_SAMPLE(obj)); } else { return (NULL); } } libinstpatch-1.1.6/libinstpatch/IpatchGigSample.h000066400000000000000000000043111400263525300220400ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_GIG_SAMPLE_H__ #define __IPATCH_GIG_SAMPLE_H__ #include #include /* forward type declarations */ typedef struct _IpatchGigSample IpatchGigSample; typedef struct _IpatchGigSampleClass IpatchGigSampleClass; #include #define IPATCH_TYPE_GIG_SAMPLE (ipatch_gig_sample_get_type ()) #define IPATCH_GIG_SAMPLE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_GIG_SAMPLE, \ IpatchGigSample)) #define IPATCH_GIG_SAMPLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_GIG_SAMPLE, \ IpatchGigSampleClass)) #define IPATCH_IS_GIG_SAMPLE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_GIG_SAMPLE)) #define IPATCH_IS_GIG_SAMPLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_GIG_SAMPLE)) #define IPATCH_GIG_SAMPLE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_GIG_SAMPLE, \ IpatchGigSampleClass)) /* GigaSampler sample object */ struct _IpatchGigSample { IpatchDLS2Sample parent_instance; guint32 group_number; /* sample group number - FIXME - what exactly is it? */ }; struct _IpatchGigSampleClass { IpatchDLS2SampleClass parent_class; }; GType ipatch_gig_sample_get_type(void); IpatchGigSample *ipatch_gig_sample_new(void); IpatchGigSample *ipatch_gig_sample_first(IpatchIter *iter); IpatchGigSample *ipatch_gig_sample_next(IpatchIter *iter); #endif libinstpatch-1.1.6/libinstpatch/IpatchGigSubRegion.c000066400000000000000000000466241400263525300225240ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchGigSubRegion * @short_description: GigaSampler sub region object * @see_also: #IpatchGigRegion * @stability: Stable * * Defines a GigaSampler sub region object which are children of * #IpatchGigRegion objects and define how a referenced #IpatchGigSample * is synthesized in a #IpatchGigInst. */ #include #include #include "IpatchGigSubRegion.h" #include "IpatchSample.h" #include "IpatchTypeProp.h" #include "ipatch_priv.h" enum { PROP_0, PROP_TITLE, PROP_LINK_ITEM, PROP_SAMPLE_INFO_OVERRIDE, PROP_SAMPLE_SIZE, PROP_SAMPLE_FORMAT, PROP_SAMPLE_RATE, PROP_SAMPLE_DATA }; static void ipatch_gig_sub_region_sample_iface_init(IpatchSampleIface *sample_iface); static gboolean ipatch_gig_sub_region_sample_iface_open(IpatchSampleHandle *handle, GError **err); static void ipatch_gig_sub_region_class_init(IpatchGigSubRegionClass *klass); static void ipatch_gig_sub_region_get_title(IpatchGigSubRegion *gig_region, GValue *value); static void ipatch_gig_sub_region_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_gig_sub_region_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_gig_sub_region_init(IpatchGigSubRegion *gig_region); static void ipatch_gig_sub_region_finalize(GObject *gobject); static void ipatch_gig_sub_region_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static void ipatch_gig_sub_region_item_remove_full(IpatchItem *item, gboolean full); static void ipatch_gig_sub_region_real_set_sample(IpatchGigSubRegion *subregion, IpatchGigSample *sample, gboolean sample_notify); static void ipatch_gig_sub_region_get_sample_info(IpatchGigSubRegion *subregion, IpatchDLS2SampleInfo *info); static GParamSpec *link_item_pspec; G_DEFINE_TYPE_WITH_CODE(IpatchGigSubRegion, ipatch_gig_sub_region, IPATCH_TYPE_ITEM, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_gig_sub_region_sample_iface_init)) /* sample interface initialization */ static void ipatch_gig_sub_region_sample_iface_init(IpatchSampleIface *sample_iface) { sample_iface->open = ipatch_gig_sub_region_sample_iface_open; sample_iface->loop_types = ipatch_sample_loop_types_standard_release; } static gboolean ipatch_gig_sub_region_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchGigSubRegion *region = IPATCH_GIG_SUB_REGION(handle->sample); g_return_val_if_fail(region->sample != NULL, FALSE); return (ipatch_sample_handle_cascade_open(handle, IPATCH_SAMPLE(region->sample), err)); } static void ipatch_gig_sub_region_class_init(IpatchGigSubRegionClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->finalize = ipatch_gig_sub_region_finalize; obj_class->get_property = ipatch_gig_sub_region_get_property; item_class->item_set_property = ipatch_gig_sub_region_set_property; item_class->copy = ipatch_gig_sub_region_item_copy; item_class->remove_full = ipatch_gig_sub_region_item_remove_full; /* use parent's mutex (IpatchGigRegion) */ item_class->mutex_slave = TRUE; g_object_class_override_property(obj_class, PROP_TITLE, "title"); link_item_pspec = g_param_spec_object("link-item", _("Link item"), _("Link item"), IPATCH_TYPE_GIG_SAMPLE, G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_LINK_ITEM, link_item_pspec); g_object_class_install_property(obj_class, PROP_SAMPLE_INFO_OVERRIDE, g_param_spec_boolean("sample-info-override", _("Sample info override"), _("Override sample info"), FALSE, G_PARAM_READWRITE)); /* IpatchSample interface properties */ ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_SIZE, "sample-size"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_FORMAT, "sample-format"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_RATE, "sample-rate"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_DATA, "sample-data"); ipatch_dls2_sample_info_install_class_properties(obj_class); } static void ipatch_gig_sub_region_get_title(IpatchGigSubRegion *gig_region, GValue *value) { IpatchGigSample *sample; char *s = NULL; g_object_get(gig_region, "link-item", &sample, NULL); /* ++ ref sample */ if(sample) { g_object_get(sample, "name", &s, NULL); /* caller takes over alloc. */ g_object_unref(sample); /* -- unref sample */ } g_value_take_string(value, s); } static void ipatch_gig_sub_region_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchGigSubRegion *subregion = IPATCH_GIG_SUB_REGION(object); IpatchDLS2SampleInfo saminfo = IPATCH_DLS2_SAMPLE_INFO_INIT; IpatchDLS2SampleInfo oldinfo, newinfo; gboolean retval, is_samprop; switch(property_id) { case PROP_LINK_ITEM: ipatch_gig_sub_region_real_set_sample(subregion, IPATCH_GIG_SAMPLE (g_value_get_object(value)), FALSE); break; case PROP_SAMPLE_INFO_OVERRIDE: ipatch_gig_sub_region_get_sample_info(subregion, &oldinfo); if(g_value_get_boolean(value)) ipatch_item_set_flags((IpatchItem *)subregion, IPATCH_GIG_SUB_REGION_SAMPLE_INFO_OVERRIDE); else ipatch_item_clear_flags((IpatchItem *)subregion, IPATCH_GIG_SUB_REGION_SAMPLE_INFO_OVERRIDE); ipatch_gig_sub_region_get_sample_info(subregion, &newinfo); ipatch_dls2_sample_info_notify_changes((IpatchItem *)subregion, &newinfo, &oldinfo); break; default: is_samprop = ipatch_dls2_sample_info_is_property_id_valid(property_id); /* check if region override info valid but override flag not set. If so then copy sample info to static 'saminfo'. OK to test region without locking it (worst that happens is default values get used). */ if(is_samprop && subregion->sample_info && !(ipatch_item_get_flags(subregion) & IPATCH_GIG_SUB_REGION_SAMPLE_INFO_OVERRIDE)) { ipatch_gig_sub_region_get_sample_info(subregion, &saminfo); } IPATCH_ITEM_WLOCK(subregion); /* is override sample_info valid but override flag not set and it is in fact a sample info property? - Copy values from sample (or defaults) */ if(is_samprop && subregion->sample_info && !(ipatch_item_get_flags(subregion) & IPATCH_GIG_SUB_REGION_SAMPLE_INFO_OVERRIDE)) { *subregion->sample_info = saminfo; } retval = ipatch_dls2_sample_info_set_property(&subregion->sample_info, property_id, value); if(retval) /* sample info set, set override flag */ ipatch_item_set_flags(subregion, IPATCH_GIG_SUB_REGION_SAMPLE_INFO_OVERRIDE); IPATCH_ITEM_WUNLOCK(subregion); if(!retval) { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } break; } } static void ipatch_gig_sub_region_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchGigSubRegion *subregion = IPATCH_GIG_SUB_REGION(object); IpatchDLS2Sample *sample = NULL; gboolean retval = FALSE, get_from_sample = FALSE; switch(property_id) { case PROP_TITLE: ipatch_gig_sub_region_get_title(subregion, value); break; case PROP_LINK_ITEM: g_value_take_object(value, ipatch_gig_sub_region_get_sample(subregion)); break; case PROP_SAMPLE_INFO_OVERRIDE: g_value_set_boolean(value, (ipatch_item_get_flags((IpatchItem *)subregion) & IPATCH_GIG_SUB_REGION_SAMPLE_INFO_OVERRIDE) != 0); break; case PROP_SAMPLE_SIZE: /* ++ ref sample */ sample = (IpatchDLS2Sample *)ipatch_gig_sub_region_get_sample(subregion); g_return_if_fail(sample != NULL); g_object_get_property((GObject *)sample, "sample-size", value); g_object_unref(sample); /* -- unref sample */ break; case PROP_SAMPLE_FORMAT: /* ++ ref sample */ sample = (IpatchDLS2Sample *)ipatch_gig_sub_region_get_sample(subregion); g_return_if_fail(sample != NULL); g_object_get_property((GObject *)sample, "sample-format", value); g_object_unref(sample); /* -- unref sample */ break; case PROP_SAMPLE_RATE: /* ++ ref sample */ sample = (IpatchDLS2Sample *)ipatch_gig_sub_region_get_sample(subregion); g_return_if_fail(sample != NULL); g_object_get_property((GObject *)sample, "sample-rate", value); g_object_unref(sample); /* -- unref sample */ break; case PROP_SAMPLE_DATA: /* ++ ref sample */ sample = (IpatchDLS2Sample *)ipatch_gig_sub_region_get_sample(subregion); g_return_if_fail(sample != NULL); g_object_get_property((GObject *)sample, "sample-data", value); g_object_unref(sample); /* -- unref sample */ break; default: /* a sample info property? */ if(property_id >= IPATCH_DLS2_SAMPLE_INFO_FIRST_PROPERTY_ID && property_id < (IPATCH_DLS2_SAMPLE_INFO_FIRST_PROPERTY_ID + IPATCH_DLS2_SAMPLE_INFO_PROPERTY_COUNT)) { IPATCH_ITEM_RLOCK(subregion); /* sample override info is valid? */ if((ipatch_item_get_flags(subregion) & IPATCH_GIG_SUB_REGION_SAMPLE_INFO_OVERRIDE) && subregion->sample_info) retval = ipatch_dls2_sample_info_get_property(subregion->sample_info, property_id, value); else { get_from_sample = TRUE; sample = (IpatchDLS2Sample *)(subregion->sample); } IPATCH_ITEM_RUNLOCK(subregion); } if(get_from_sample) { if(sample) { IPATCH_ITEM_RLOCK(sample); ipatch_dls2_sample_info_get_property(sample->sample_info, property_id, value); IPATCH_ITEM_RUNLOCK(sample); g_object_unref(sample); } else ipatch_dls2_sample_info_get_property(sample->sample_info, property_id, value); } else if(!retval) { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } break; } } static void ipatch_gig_sub_region_init(IpatchGigSubRegion *subregion) { ipatch_gig_effects_init(&subregion->effects); } static void ipatch_gig_sub_region_finalize(GObject *gobject) { IpatchGigSubRegion *subregion = IPATCH_GIG_SUB_REGION(gobject); IPATCH_ITEM_WLOCK(subregion); if(subregion->sample) { g_object_unref(subregion->sample); } if(subregion->sample_info) { ipatch_dls2_sample_info_free(subregion->sample_info); } subregion->sample = NULL; subregion->sample_info = NULL; IPATCH_ITEM_WUNLOCK(subregion); if(G_OBJECT_CLASS(ipatch_gig_sub_region_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_gig_sub_region_parent_class)->finalize(gobject); } } static void ipatch_gig_sub_region_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchGigSubRegion *src_sub, *dest_sub; IpatchGigSample *sample; src_sub = IPATCH_GIG_SUB_REGION(src); dest_sub = IPATCH_GIG_SUB_REGION(dest); IPATCH_ITEM_RLOCK(src_sub); dest_sub->effects = src_sub->effects; if(src_sub->sample) { sample = (IpatchGigSample *) IPATCH_ITEM_COPY_LINK_FUNC(IPATCH_ITEM(dest_sub), IPATCH_ITEM(src_sub->sample), link_func, user_data); if(sample) { dest_sub->sample = g_object_ref(sample); /* ++ ref sample */ } } if(src_sub->sample_info) dest_sub->sample_info = ipatch_dls2_sample_info_duplicate(src_sub->sample_info); IPATCH_ITEM_RUNLOCK(src_sub); } static void ipatch_gig_sub_region_item_remove_full(IpatchItem *item, gboolean full) { if(full) { ipatch_gig_sub_region_real_set_sample(IPATCH_GIG_SUB_REGION(item), NULL, TRUE); } if(IPATCH_ITEM_CLASS(ipatch_gig_sub_region_parent_class)->remove_full) { IPATCH_ITEM_CLASS(ipatch_gig_sub_region_parent_class)->remove_full(item, full); } } /** * ipatch_gig_sub_region_new: * * Create a new GigaSampler sub region. * * Returns: New GigaSampler sub region with a ref count of 1 which the caller * owns. */ IpatchGigSubRegion * ipatch_gig_sub_region_new(void) { return (IPATCH_GIG_SUB_REGION(g_object_new(IPATCH_TYPE_GIG_SUB_REGION, NULL))); } /** * ipatch_gig_sub_region_first: (skip) * @iter: Patch item iterator containing #IpatchGigSubRegion items * * Gets the first item in a sub region iterator. A convenience * wrapper for ipatch_iter_first(). * * Returns: The first sub region in @iter or %NULL if empty. */ IpatchGigSubRegion * ipatch_gig_sub_region_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_GIG_SUB_REGION(obj)); } else { return (NULL); } } /** * ipatch_gig_sub_region_next: (skip) * @iter: Patch item iterator containing #IpatchGigSubRegion items * * Gets the next item in a sub region iterator. A convenience wrapper * for ipatch_iter_next(). * * Returns: The next sub region in @iter or %NULL if at the end of * the list. */ IpatchGigSubRegion * ipatch_gig_sub_region_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_GIG_SUB_REGION(obj)); } else { return (NULL); } } /** * ipatch_gig_sub_region_get_sample: * @subregion: GigaSampler sub region to get sample of * * Returns: (transfer full): The sub region's sample, referenced for the caller, unref * it when finished. */ IpatchGigSample * ipatch_gig_sub_region_get_sample(IpatchGigSubRegion *subregion) { IpatchGigSample *sample; g_return_val_if_fail(IPATCH_IS_GIG_SUB_REGION(subregion), NULL); IPATCH_ITEM_RLOCK(subregion); sample = g_object_ref(subregion->sample); IPATCH_ITEM_WUNLOCK(subregion); return (sample); } /** * ipatch_gig_sub_region_set_sample: * @subregion: GigaSampler sub region to set sample of * @sample: Sample to assign to the sub region * * Set the referenced sample of a sub region. */ void ipatch_gig_sub_region_set_sample(IpatchGigSubRegion *subregion, IpatchGigSample *sample) { g_return_if_fail(IPATCH_IS_GIG_SUB_REGION(subregion)); g_return_if_fail(IPATCH_IS_GIG_SAMPLE(sample)); ipatch_gig_sub_region_real_set_sample(subregion, sample, TRUE); } static void ipatch_gig_sub_region_real_set_sample(IpatchGigSubRegion *subregion, IpatchGigSample *sample, gboolean sample_notify) { GValue newval = { 0 }, oldval = { 0 }; IpatchDLS2SampleInfo oldinfo, newinfo; IpatchGigSample *oldsample; /* get all values of current sample info */ ipatch_gig_sub_region_get_sample_info(subregion, &oldinfo); if(sample) { g_object_ref(sample); } IPATCH_ITEM_WLOCK(subregion); oldsample = subregion->sample; subregion->sample = sample; IPATCH_ITEM_WUNLOCK(subregion); if(sample_notify) { g_value_init(&oldval, IPATCH_TYPE_GIG_SAMPLE); g_value_take_object(&oldval, oldsample); g_value_init(&newval, IPATCH_TYPE_GIG_SAMPLE); g_value_set_object(&newval, sample); ipatch_item_prop_notify((IpatchItem *)subregion, link_item_pspec, &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } else if(oldsample) { g_object_unref(oldsample); /* -- unref old sample */ } /* do the title notify */ g_value_init(&newval, G_TYPE_STRING); ipatch_gig_sub_region_get_title(subregion, &newval); ipatch_item_prop_notify((IpatchItem *)subregion, ipatch_item_pspec_title, &newval, NULL); g_value_unset(&newval); /* notify for sample info properties */ ipatch_gig_sub_region_get_sample_info(subregion, &newinfo); ipatch_dls2_sample_info_notify_changes((IpatchItem *)subregion, &newinfo, &oldinfo); } /* fetch active sample info from a Gig sub region */ static void ipatch_gig_sub_region_get_sample_info(IpatchGigSubRegion *subregion, IpatchDLS2SampleInfo *info) { IpatchDLS2Sample *sample = NULL; gboolean info_set = TRUE; IPATCH_ITEM_RLOCK(subregion); if(ipatch_item_get_flags(subregion) & IPATCH_GIG_SUB_REGION_SAMPLE_INFO_OVERRIDE && subregion->sample_info) { *info = *subregion->sample_info; } else if(subregion->sample) { sample = (IpatchDLS2Sample *)g_object_ref(subregion->sample); } else { info_set = FALSE; } IPATCH_ITEM_RUNLOCK(subregion); if(sample) { IPATCH_ITEM_RLOCK(sample); if(sample->sample_info) { *info = *sample->sample_info; } else { info_set = FALSE; } IPATCH_ITEM_RUNLOCK(sample); g_object_unref(sample); } if(!info_set) { ipatch_dls2_sample_info_init(info); } } libinstpatch-1.1.6/libinstpatch/IpatchGigSubRegion.h000066400000000000000000000061061400263525300225200ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_GIG_SUB_REGION_H__ #define __IPATCH_GIG_SUB_REGION_H__ #include #include /* forward type declarations */ typedef struct _IpatchGigSubRegion IpatchGigSubRegion; typedef struct _IpatchGigSubRegionClass IpatchGigSubRegionClass; #include #include #include #define IPATCH_TYPE_GIG_SUB_REGION (ipatch_gig_sub_region_get_type ()) #define IPATCH_GIG_SUB_REGION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_GIG_SUB_REGION, \ IpatchGigSubRegion)) #define IPATCH_GIG_SUB_REGION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_GIG_SUB_REGION, \ IpatchGigSubRegionClass)) #define IPATCH_IS_GIG_SUB_REGION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_GIG_SUB_REGION)) #define IPATCH_IS_GIG_SUB_REGION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_GIG_SUB_REGION)) #define IPATCH_GIG_SUB_REGION_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_GIG_SUB_REGION, \ IpatchGigSubRegionClass)) /* a GigaSampler sub region */ struct _IpatchGigSubRegion { IpatchItem parent_instance; IpatchGigEffects effects; /* effects for this sub region */ IpatchGigSample *sample; /* sample for this sub region */ IpatchDLS2SampleInfo *sample_info; /* sample info override or NULL */ }; /* GigaSampler sub region class */ struct _IpatchGigSubRegionClass { IpatchItemClass parent_class; }; /* Flags crammed into IpatchItem flags */ typedef enum { IPATCH_GIG_SUB_REGION_SAMPLE_INFO_OVERRIDE = 1 << IPATCH_ITEM_UNUSED_FLAG_SHIFT } IpatchGigSubRegionFlags; /** * IPATCH_GIG_SUB_REGION_UNUSED_FLAG_SHIFT: (skip) */ /* 1 flag + 3 for expansion */ #define IPATCH_GIG_SUB_REGION_UNUSED_FLAG_SHIFT \ (IPATCH_ITEM_UNUSED_FLAG_SHIFT + 4) GType ipatch_gig_sub_region_get_type(void); IpatchGigSubRegion *ipatch_gig_sub_region_new(void); IpatchGigSubRegion *ipatch_gig_sub_region_first(IpatchIter *iter); IpatchGigSubRegion *ipatch_gig_sub_region_next(IpatchIter *iter); IpatchGigSample *ipatch_gig_sub_region_get_sample(IpatchGigSubRegion *subregion); void ipatch_gig_sub_region_set_sample(IpatchGigSubRegion *subregion, IpatchGigSample *sample); #endif libinstpatch-1.1.6/libinstpatch/IpatchItem.c000066400000000000000000001533071400263525300210730ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchItem * @short_description: Abstract base item object * @see_also: * @stability: Stable * * The abstract base item type from which all instrument objects are derived * and many other object types as well. */ #include #include #include #include "IpatchItem.h" #include "IpatchIter.h" #include "IpatchContainer.h" #include "IpatchBase.h" /* IPATCH_BASE_CHANGED flag */ #include "IpatchParamProp.h" #include "IpatchTypeProp.h" #include "ipatch_priv.h" #include "i18n.h" #include "util.h" enum { PROP_0, PROP_FLAGS, PROP_PARENT, PROP_BASE, PROP_TITLE }; /* for unique property caching */ typedef struct { GParamSpec **pspecs; /* unique prop paramspecs (groups are consecutive) */ guint32 groups; /* 1 bit per pspec, pspecs of same group are the same */ } UniqueBag; /* a private bag structure for ipatch_item_set_parent */ typedef struct { IpatchItem *base; int hooks_active; } SetParentBag; /* defined in IpatchItemProp.c */ extern void _ipatch_item_prop_init(void); extern void _ipatch_item_prop_deinit(void); /* defined in IpatchBase.c */ extern GParamSpec *ipatch_base_pspec_changed; static gboolean _ipatch_item_hash_free_value(gpointer key, UniqueBag *value, gpointer user_data); static void ipatch_item_base_init(IpatchItemClass *klass); static void ipatch_item_class_init(IpatchItemClass *klass); static void ipatch_item_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_item_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_item_set_property_override(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_item_init(IpatchItem *item); static void ipatch_item_finalize(GObject *object); static void ipatch_item_item_remove_full(IpatchItem *item, gboolean full); static void ipatch_item_recursive_base_set(IpatchContainer *container, SetParentBag *bag); static void ipatch_item_recursive_base_unset(IpatchContainer *container); static void ipatch_item_real_remove_full(IpatchItem *item, gboolean full); static void ipatch_item_real_remove_recursive(IpatchItem *item, gboolean full); static void copy_hash_to_list_GHFunc(gpointer key, gpointer value, gpointer user_data); static UniqueBag *item_lookup_unique_bag(GType type); static gint unique_group_list_sort_func(gconstpointer a, gconstpointer b); static GObjectClass *parent_class = NULL; /* item class for fast hook function method calls */ static IpatchItemClass *real_item_class = NULL; /* define the lock for the unique property cache hash */ G_LOCK_DEFINE_STATIC(unique_prop_cache); /* cache of IpatchItem unique properties (hash: GType => UniqueBag) */ static GHashTable *unique_prop_cache = NULL; /* store title property GParamSpec as a convenience to derived types */ /* Useful when libinstpatch library is used as a static library. */ GParamSpec *ipatch_item_pspec_title = NULL; /* ----- Initialization/deinitialization of property change callback system -*/ /* Initialization */ void _ipatch_item_init(void) { /* cache of IpatchItem unique properties (hash: GType => UniqueBag) */ unique_prop_cache = g_hash_table_new (NULL, NULL); /* Initialize property change callback system */ _ipatch_item_prop_init(); } /* Free property change callback system */ void _ipatch_item_deinit(void) { /* free property change callback system */ _ipatch_item_prop_deinit(); g_hash_table_foreach_remove(unique_prop_cache, (GHRFunc)_ipatch_item_hash_free_value, NULL); g_hash_table_destroy(unique_prop_cache); } /* free hash value entry */ static gboolean _ipatch_item_hash_free_value(gpointer key, UniqueBag *value, gpointer user_data) { g_free(value->pspecs); g_free(value); return TRUE; } /* ----- IpatchItem object functions ---------------------------------------*/ /* Getter function returning title property GParamSpec as a convenience to derived type. Useful when libinstpatch library is used as a shared library linked at load time. */ GParamSpec * ipatch_item_get_pspec_title(void) { return ipatch_item_pspec_title; } GType ipatch_item_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchItemClass), (GBaseInitFunc) ipatch_item_base_init, NULL, (GClassInitFunc) ipatch_item_class_init, NULL, NULL, sizeof(IpatchItem), 0, (GInstanceInitFunc)ipatch_item_init, }; item_type = g_type_register_static(G_TYPE_OBJECT, "IpatchItem", &item_info, G_TYPE_FLAG_ABSTRACT); } return (item_type); } static void ipatch_item_base_init(IpatchItemClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); /* override the set property method for all derived IpatchItem types */ obj_class->set_property = ipatch_item_set_property_override; } /* set_property override method that calls item_set_property method, derived * classes should use the item_set_property method instead of set_property. */ static void ipatch_item_set_property_override(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchItemClass *klass; GObjectClass *obj_class; gboolean hooks_active; GValue oldval = { 0 }; GType type, prev_type; /* Get the class for the owning type of this parameter. * NOTE: This wont work for interfaces (handled below) or overridden * properties. FIXME - Not sure how to handle overridden properties. */ klass = g_type_class_peek(pspec->owner_type); /* property belongs to an interface? */ if(!klass && G_TYPE_IS_INTERFACE(pspec->owner_type)) { /* Unfortunately GObject doesn't give us an easy way to determine which * class implements an interface in an object's derived ancestry, so we're * left with this hack.. */ prev_type = G_OBJECT_TYPE(object); type = prev_type; /* find type in object's ancestry that implements the interface by searching * for the type just before the first parent which doesn't conform to the * interface. */ while((type = g_type_parent(type))) { if(!g_type_is_a(type, pspec->owner_type)) { break; } prev_type = type; } if(prev_type) { klass = g_type_class_peek(prev_type); } } g_return_if_fail(klass != NULL); g_return_if_fail(klass->item_set_property != NULL); /* hook functions can be inactive for greater performance */ hooks_active = (ipatch_item_get_flags(object) & IPATCH_ITEM_HOOKS_ACTIVE) != 0; /* fetch parameter's current value to use as oldval in property notify */ if(hooks_active) { obj_class = (GObjectClass *)klass; g_return_if_fail(obj_class->get_property != NULL); g_value_init(&oldval, G_PARAM_SPEC_VALUE_TYPE(pspec)); obj_class->get_property(object, property_id, &oldval, pspec); } klass->item_set_property(object, property_id, value, pspec); /* call property change */ if(hooks_active) { ipatch_item_prop_notify((IpatchItem *)object, pspec, value, &oldval); g_value_unset(&oldval); } } static void ipatch_item_class_init(IpatchItemClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); real_item_class = klass; parent_class = g_type_class_peek_parent(klass); klass->item_set_property = ipatch_item_set_property; obj_class->get_property = ipatch_item_get_property; obj_class->finalize = ipatch_item_finalize; klass->remove_full = ipatch_item_item_remove_full; g_object_class_install_property(obj_class, PROP_FLAGS, g_param_spec_uint("flags", _("Flags"), _("Flags"), 0, G_MAXUINT, 0, G_PARAM_READWRITE | IPATCH_PARAM_HIDE | IPATCH_PARAM_NO_SAVE_CHANGE | IPATCH_PARAM_NO_SAVE)); g_object_class_install_property(obj_class, PROP_PARENT, g_param_spec_object("parent", _("Parent"), _("Parent"), IPATCH_TYPE_ITEM, G_PARAM_READWRITE | IPATCH_PARAM_NO_SAVE)); g_object_class_install_property(obj_class, PROP_BASE, g_param_spec_object("base", _("Base"), _("Base"), IPATCH_TYPE_BASE, G_PARAM_READABLE | IPATCH_PARAM_NO_SAVE)); ipatch_item_pspec_title = g_param_spec_string("title", _("Title"), _("Title"), NULL, G_PARAM_READABLE | IPATCH_PARAM_NO_SAVE_CHANGE | IPATCH_PARAM_NO_SAVE); g_object_class_install_property(obj_class, PROP_TITLE, ipatch_item_pspec_title); } static void ipatch_item_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchItem *item = IPATCH_ITEM(object); switch(property_id) { case PROP_FLAGS: ipatch_item_set_flags(item, g_value_get_uint(value)); break; case PROP_PARENT: ipatch_item_set_parent(item, IPATCH_ITEM(g_value_get_object(value))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_item_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchItem *item = IPATCH_ITEM(object); char *name; switch(property_id) { case PROP_FLAGS: g_value_set_uint(value, ipatch_item_get_flags(item)); break; case PROP_PARENT: g_value_take_object(value, ipatch_item_get_parent(item)); break; case PROP_BASE: g_value_take_object(value, ipatch_item_get_base(item)); break; case PROP_TITLE: /* see if the "name" type property is set */ ipatch_type_object_get((GObject *)item, "name", &name, NULL); if(name) { g_value_take_string(value, name); } else { g_value_set_string(value, IPATCH_UNTITLED); /* "untitled" */ } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_item_init(IpatchItem *item) { /* always assign a mutex, will be freed and set to parent's mutex if the class has mutex_slave set (during ipatch_item_set_parent) */ item->mutex = g_malloc(sizeof(GStaticRecMutex)); g_static_rec_mutex_init(item->mutex); ipatch_item_set_flags(item, IPATCH_ITEM_FREE_MUTEX); } static void ipatch_item_finalize(GObject *object) { IpatchItem *item = IPATCH_ITEM(object); if(ipatch_item_get_flags(item) & IPATCH_ITEM_FREE_MUTEX) { g_static_rec_mutex_free(item->mutex); g_free(item->mutex); } item->mutex = NULL; if(parent_class->finalize) { parent_class->finalize(object); } } static void ipatch_item_item_remove_full(IpatchItem *item, gboolean full) { IpatchItem *parent; parent = ipatch_item_get_parent(item); // ++ ref parent if(parent) { ipatch_container_remove(IPATCH_CONTAINER(parent), item); g_object_unref(parent); // -- unref parent } } /** * ipatch_item_get_flags: * @item: (type Ipatch.Item): #IpatchItem to get flag field from * * Get the value of the flags field in a patch item. * * Returns: Value of flags field (some of which is user definable). */ int ipatch_item_get_flags(gpointer item) { g_return_val_if_fail(IPATCH_IS_ITEM(item), 0); return (item ? g_atomic_int_get(&((IpatchItem *)item)->flags) : 0); } /** * ipatch_item_set_flags: * @item: (type Ipatch.Item): #IpatchItem to set flags in * @flags: Flags to set * * Set flags in a patch item. All bits that are set in @flags are set in * the @item flags field. */ void ipatch_item_set_flags(gpointer item, int flags) { int oldval, newval; g_return_if_fail(IPATCH_IS_ITEM(item)); do { oldval = g_atomic_int_get(&((IpatchItem *)item)->flags); newval = oldval | flags; } while(!g_atomic_int_compare_and_exchange(&((IpatchItem *)item)->flags, oldval, newval)); } /** * ipatch_item_clear_flags: * @item: (type Ipatch.Item): #IpatchItem object to clear flags in * @flags: Flags to clear * * Clear (unset) flags in a patch item. All bits that are set in @flags are * cleared in the @item flags field. */ void ipatch_item_clear_flags(gpointer item, int flags) { int oldval, newval; g_return_if_fail(IPATCH_IS_ITEM(item)); do { oldval = g_atomic_int_get(&((IpatchItem *)item)->flags); newval = oldval & ~flags; } while(!g_atomic_int_compare_and_exchange(&((IpatchItem *)item)->flags, oldval, newval)); } /** * ipatch_item_set_parent: * @item: Item to set parent of (should not have a parent). * @parent: New parent of item. * * Usually only used by #IpatchItem type implementations. Sets the * parent of a patch item. Also recursively sets base parent and * #IPATCH_ITEM_HOOKS_ACTIVE flag if set in @parent. Also assigns the item's * thread mutex to that of the parent if its class has mutex_slave set. * The @parent container is responsible for adding a reference to @item - this * function does not do so. * * NOTE: If the @item has mutex_slave set in its class then the item will use * @parent object's mutex. During the call to this function @item should not * be accessed by any other threads, otherwise problems may occur when the * mutex is changed. Normally this shouldn't be much of an issue, since new * item's are often only accessed by 1 thread until being parented. */ void ipatch_item_set_parent(IpatchItem *item, IpatchItem *parent) { IpatchItemClass *klass; gboolean is_container; SetParentBag bag; guint depth; guint i; g_return_if_fail(IPATCH_IS_ITEM(item)); g_return_if_fail(IPATCH_IS_ITEM(parent)); g_return_if_fail(item != parent); bag.base = ipatch_item_get_base(parent); /* ++ ref base patch item */ is_container = IPATCH_IS_CONTAINER(item); /* get value of parent's hook flag */ bag.hooks_active = ipatch_item_get_flags(parent) & IPATCH_ITEM_HOOKS_ACTIVE; IPATCH_ITEM_WLOCK(item); if(log_if_fail(item->parent == NULL)) { IPATCH_ITEM_WUNLOCK(item); if(bag.base) { g_object_unref(bag.base); } return; } klass = IPATCH_ITEM_GET_CLASS(item); if(klass->mutex_slave) { depth = g_static_rec_mutex_unlock_full(item->mutex); if(ipatch_item_get_flags(item) & IPATCH_ITEM_FREE_MUTEX) { g_static_rec_mutex_free(item->mutex); g_free(item->mutex); } item->mutex = parent->mutex; ipatch_item_clear_flags(item, IPATCH_ITEM_FREE_MUTEX); /* lock it the number of times old mutex was locked, we don't use g_static_rec_mutex_lock_full because it could set depth rather than add to it */ for(i = 0; i < depth; i++) { g_static_rec_mutex_lock(item->mutex); } } item->parent = parent; if(bag.base) { item->base = bag.base; /* inherit base parent item if set */ } /* inherit active hooks flag, has no effect if not set */ ipatch_item_set_flags(item, bag.hooks_active); /* if item is a container and base or hooks active flag is set, recursively set children hooks active flags */ if(is_container && (bag.base || bag.hooks_active)) { ipatch_item_recursive_base_set((IpatchContainer *)item, &bag); } IPATCH_ITEM_WUNLOCK(item); if(bag.base) { g_object_unref(bag.base); /* -- unref base patch item */ } } /* recursively sets base field of a tree of items, expects container to be write locked */ static void ipatch_item_recursive_base_set(IpatchContainer *container, SetParentBag *bag) { IpatchIter iter; GObject *child; const GType *types; /* get container child list types */ types = ipatch_container_get_child_types(container); while(*types) /* loop over list types */ { /* initialize iterator to child list */ if(!ipatch_container_init_iter(container, &iter, *types)) { return; } child = ipatch_iter_first(&iter); while(child) /* loop over child list */ { IPATCH_ITEM_WLOCK(child); if(bag->base) /* inherit base pointer if set */ { ((IpatchItem *)child)->base = bag->base; } /* inherit active hooks flag, has no effect if not set */ ipatch_item_set_flags(child, bag->hooks_active); if(IPATCH_IS_CONTAINER(child)) /* recurse if a container */ { ipatch_item_recursive_base_set((IpatchContainer *)child, bag); } IPATCH_ITEM_WUNLOCK(child); child = ipatch_iter_next(&iter); } /* child loop */ types++; } /* child type loop */ } /** * ipatch_item_unparent: * @item: Item to unparent * * Usually only used by #IpatchItem type implementations. Unparent an * item. Also recursively unsets base parent and * #IPATCH_ITEM_HOOKS_ACTIVE flag. Parent container object is * responsible for removing reference of @item - this function does * not do so. */ void ipatch_item_unparent(IpatchItem *item) { gboolean is_container; g_return_if_fail(IPATCH_IS_ITEM(item)); is_container = IPATCH_IS_CONTAINER(item); IPATCH_ITEM_WLOCK(item); if(!item->parent) { IPATCH_ITEM_WUNLOCK(item); return; } /* clear parent, base and active hooks flag of item */ item->parent = NULL; item->base = NULL; ipatch_item_clear_flags(item, IPATCH_ITEM_HOOKS_ACTIVE); /* recursively unset base field and active hooks flag of all children */ if(is_container) { ipatch_item_recursive_base_unset((IpatchContainer *)item); } IPATCH_ITEM_WUNLOCK(item); } /* recursively unsets base field of a tree of items, expects container to be write locked */ static void ipatch_item_recursive_base_unset(IpatchContainer *container) { IpatchIter iter; GObject *child; const GType *types; types = ipatch_container_get_child_types(container); while(*types) /* loop over list types */ { /* initialize iterator to child list */ if(!ipatch_container_init_iter(container, &iter, *types)) { return; } child = ipatch_iter_first(&iter); while(child) /* loop over child list */ { IPATCH_ITEM_WLOCK(child); /* unset base pointer and clear active hooks flag */ ((IpatchItem *)child)->base = NULL; ipatch_item_clear_flags(child, IPATCH_ITEM_HOOKS_ACTIVE); if(IPATCH_IS_CONTAINER(child)) /* recurse if a container */ { ipatch_item_recursive_base_unset((IpatchContainer *)child); } IPATCH_ITEM_WUNLOCK(child); child = ipatch_iter_next(&iter); } /* child loop */ types++; } /* child type loop */ } /** * ipatch_item_get_parent: * @item: Item to get parent of * * Gets the parent of @item after incrementing its reference count. The * caller is responsible for decrementing the reference count with * g_object_unref when finished with it. This function is useful because * all code paths must contain references to the items that they are working * with in a multi-thread environment (there is no guarantee an item won't * be destroyed unless a refcount is held). * * Returns: (transfer full): The parent of @item or %NULL if not parented or a root item. If * not %NULL then the reference count of parent has been incremented and the * caller is responsible for removing it when finished with parent. */ IpatchItem * ipatch_item_get_parent(IpatchItem *item) { IpatchItem *parent; g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL); IPATCH_ITEM_RLOCK(item); parent = item->parent; if(parent) { g_object_ref(parent); } IPATCH_ITEM_RUNLOCK(item); return (parent); } /** * ipatch_item_peek_parent: (skip) * @item: Item to get the parent of * * This function is the same as ipatch_item_get_parent() (gets an * item's parent) but does not increment the parent's ref * count. Therefore this function should only be used when the caller * already holds a reference or when only the value of the pointer * will be used (not the contents), to avoid multi-thread problems. * * Returns: The parent of @item or %NULL if not parented or a root item. */ IpatchItem * ipatch_item_peek_parent(IpatchItem *item) { g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL); return (item->parent); } /** * ipatch_item_get_base: * @item: Item to get base parent of * * Gets the base parent of @item (toplevel patch file object) after * incrementing its reference count. The caller is responsible for * decrementing the reference count with g_object_unref when finished * with it. If @item is itself a #IpatchBase object then it is returned. * * Returns: (transfer full): The base parent of @item or %NULL if no #IpatchBase type * in parent ancestry. If not %NULL then the reference count of * parent has been incremented and the caller is responsible for * removing it when finished with the base parent. */ IpatchItem * ipatch_item_get_base(IpatchItem *item) { IpatchItem *base; g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL); /* return the item if it is itself an #IpatchBase object */ if(IPATCH_IS_BASE(item)) { return (g_object_ref(item)); } IPATCH_ITEM_RLOCK(item); base = item->base; if(base) { g_object_ref(base); } IPATCH_ITEM_RUNLOCK(item); return (base); } /** * ipatch_item_peek_base: (skip) * @item: Item to get the base parent of * * This function is the same as ipatch_item_get_base() (gets an * item's toplevel base parent) but does not increment the parent's ref * count. Therefore this function should only be used when the caller * already holds a reference or when only the value of the pointer * will be used (not the contents), to avoid multi-thread problems. * * Returns: The base parent of @item or %NULL if no #IpatchBase type * in parent ancestry. */ IpatchItem * ipatch_item_peek_base(IpatchItem *item) { g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL); /* return the item if it is itself an #IpatchBase object */ if(IPATCH_IS_BASE(item)) { return (item); } return (item->base); } /** * ipatch_item_get_ancestor_by_type: * @item: Item to search for parent of a given type * @ancestor_type: Type of parent in the @item object's ancestry * * Searches for the first parent item that is derived from * @ancestor_type in the @item object's ancestry. @ancestor_type must * be an #IpatchItem derived type (as well as the ancestry of * @item). Of note is that @item can also match. The returned item's * reference count has been incremented and it is the responsiblity of * the caller to remove it with g_object_unref() when finished with * it. * * Returns: (transfer full): The matched item (can be the @item itself) or %NULL if no * item matches @ancestor_type in the ancestry. Remember to unref the * matched item when done with it. */ IpatchItem * ipatch_item_get_ancestor_by_type(IpatchItem *item, GType ancestor_type) { #define MAX_ITEM_DEPTH 10 IpatchItem *p, *ancestry[MAX_ITEM_DEPTH]; int i, depth = -1; g_return_val_if_fail(IPATCH_ITEM(item), NULL); g_return_val_if_fail(g_type_is_a(ancestor_type, IPATCH_TYPE_ITEM), NULL); p = item; do { if(g_type_is_a(G_OBJECT_TYPE(p), ancestor_type)) { break; } depth++; g_assert(depth < MAX_ITEM_DEPTH); p = ancestry[depth] = ipatch_item_get_parent(p); } while(p); if(depth >= 0) /* unreference ancestry */ for(i = 0; i <= depth; i++) if(ancestry[i] != p) { g_object_unref(ancestry[i]); } if(p == item) { g_object_ref(p); } return (p); } /** * ipatch_item_peek_ancestor_by_type: (skip) * @item: Item to search for parent of a given type * @ancestor_type: Type of parent in the @item object's ancestry * * Just like ipatch_item_get_ancestor_by_type() but doesn't ref returned item. * This should only be used when caller already owns a reference or only the * pointer value is of importance, since otherwise there is a potential * multi-thread race condition. * * Returns: The matched item (can be the @item itself) or %NULL if no * item matches @ancestor_type in the ancestry. Remember that the returned * item is not referenced. */ IpatchItem * ipatch_item_peek_ancestor_by_type(IpatchItem *item, GType ancestor_type) { IpatchItem *match; match = ipatch_item_get_ancestor_by_type(item, ancestor_type); if(match) { g_object_unref(match); } return (match); } /** * ipatch_item_remove: * @item: Item to remove * * This function removes an item from its parent container and removes other * references from within the same patch (e.g., a #IpatchSF2Sample * will be removed from its parent #IpatchSF2 and all #IpatchSF2IZone objects * referencing the sample will also be removed). */ void ipatch_item_remove(IpatchItem *item) { g_return_if_fail(IPATCH_IS_ITEM(item)); ipatch_item_real_remove_full(item, FALSE); } /** * ipatch_item_remove_full: * @item: Item to remove * @full: %TRUE removes all references to and from @item, %FALSE removes only references to @item * * Like ipatch_item_remove() but will also remove all references from @item (in the same #IpatchBase * object) if @full is %TRUE, behaves like ipatch_item_remove() if @full is %FALSE. * * Since: 1.1.0 */ void ipatch_item_remove_full(IpatchItem *item, gboolean full) { g_return_if_fail(IPATCH_IS_ITEM(item)); ipatch_item_real_remove_full(item, full); } static void ipatch_item_real_remove_full(IpatchItem *item, gboolean full) { IpatchItemClass *klass; IpatchItem *parent; klass = IPATCH_ITEM_GET_CLASS(item); if(klass->remove_full) { (*klass->remove_full)(item, full); /* call the remove_full class method */ } else if(klass->remove) { (*klass->remove)(item); /* call the remove class method */ // If full removal specified and item only has older remove method, remove all children if(full && IPATCH_IS_CONTAINER(item)) { ipatch_container_remove_all(IPATCH_CONTAINER(item)); } } else { // Default remove if no class method parent = ipatch_item_get_parent(item); if(parent) // Remove item from parent { ipatch_container_remove(IPATCH_CONTAINER(parent), item); g_object_unref(parent); } // If full removal specified, remove all children if(full && IPATCH_IS_CONTAINER(item)) { ipatch_container_remove_all(IPATCH_CONTAINER(item)); } } } /** * ipatch_item_remove_recursive: * @item: Item to recursively remove * @full: %TRUE removes all references to and from items, %FALSE removes only references to items * * This function calls ipatch_item_remove_full() on @item and all of its children recursively. * * Since: 1.1.0 */ void ipatch_item_remove_recursive(IpatchItem *item, gboolean full) { g_return_if_fail(IPATCH_IS_ITEM(item)); ipatch_item_real_remove_recursive(item, full); } static void ipatch_item_real_remove_recursive(IpatchItem *item, gboolean full) { const GType *child_types, *ptype; IpatchList *list; GList *p; if(IPATCH_IS_CONTAINER(item)) { child_types = ipatch_container_get_child_types((IpatchContainer *)item); /* loop over child types */ for(ptype = child_types; *ptype; ptype++) { list = ipatch_container_get_children((IpatchContainer *)item, *ptype); /* ++ ref new list */ if(g_type_is_a(*ptype, IPATCH_TYPE_CONTAINER)) { for(p = list->items; p; p = p->next) { ipatch_item_real_remove_recursive((IpatchItem *)(p->data), full); } } else // Shortcut for non-container types (removes a level of unnecessary recursiveness) { for(p = list->items; p; p = p->next) { ipatch_item_real_remove_full((IpatchItem *)(p->data), full); } } g_object_unref(list); /* -- unref list */ } } ipatch_item_real_remove_full(item, full); } /** * ipatch_item_changed: * @item: Item that has changed * * This function should be called when an item's saveable state (what ends * up in a patch file) has changed. Causes the @item object's base parent * to have its changed flag set, indicating that the file has changes that * have not been saved. Note that this function is automatically called when * an ipatch_item_prop_notify() occurs for a property without the * #IPATCH_PARAM_NO_SAVE_CHANGE flag in its #GParamSpec and therefore this * function should not be called additionally. */ void ipatch_item_changed(IpatchItem *item) { IpatchItem *base = NULL; g_return_if_fail(IPATCH_IS_ITEM(item)); IPATCH_ITEM_RLOCK(item); if(item->base) { base = item->base; } else if(IPATCH_IS_BASE(item)) { base = item; } if(base && !(base->flags & IPATCH_BASE_CHANGED)) { g_object_ref(base); /* ++ ref base object */ ipatch_item_set_flags(base, IPATCH_BASE_CHANGED); } else { base = NULL; } IPATCH_ITEM_RUNLOCK(item); if(base) /* do property notify for base "changed" flag */ { ipatch_item_prop_notify(base, ipatch_base_pspec_changed, ipatch_util_value_bool_true, ipatch_util_value_bool_false); g_object_unref(base); /* -- unref base parent object */ } } /** * ipatch_item_get_property_fast: * @item: Item to get property value from * @pspec: Parameter spec of property to get * @value: Uninitialized caller supplied value to store the current value in. * Remember to call g_value_unset when done with it. * * Usually only used by #IpatchItem implementations. A faster way to retrieve * an object property. Often used for fetching current value for property * notifies. Notice that this function currently doesn't work with interface * or class overridden properties. */ void ipatch_item_get_property_fast(IpatchItem *item, GParamSpec *pspec, GValue *value) { GObjectClass *obj_class; g_return_if_fail(IPATCH_IS_ITEM(item)); g_return_if_fail(G_IS_PARAM_SPEC(pspec)); g_return_if_fail(value != NULL); obj_class = g_type_class_peek(pspec->owner_type); g_return_if_fail(obj_class != NULL); g_return_if_fail(obj_class->get_property != NULL); g_value_init(value, G_PARAM_SPEC_VALUE_TYPE(pspec)); obj_class->get_property((GObject *)item, IPATCH_PARAM_SPEC_ID(pspec), value, pspec); } /** * ipatch_item_copy: * @dest: New destination item to copy to (should be same type or derived * from @src type) * @src: Item to copy from * * Copy an item using the item class' "copy" method. Object links are * copied as is, meaning the resulting object is a local object to the same * #IpatchBase. */ void ipatch_item_copy(IpatchItem *dest, IpatchItem *src) { IpatchItemClass *klass; GType dest_type, src_type; g_return_if_fail(IPATCH_IS_ITEM(dest)); g_return_if_fail(IPATCH_IS_ITEM(src)); dest_type = G_OBJECT_TYPE(dest); src_type = G_OBJECT_TYPE(src); g_return_if_fail(g_type_is_a(dest_type, src_type)); klass = IPATCH_ITEM_GET_CLASS(src); g_return_if_fail(klass->copy != NULL); klass->copy(dest, src, NULL, NULL); } /** * ipatch_item_copy_link_func: * @dest: New destination item to copy to (should be same type or derived * from @src type) * @src: Item to copy from * @link_func: (scope call) (closure user_data): Function to call for each * object link pointer in @src. This function can alter the copied link if desired. * @user_data: (nullable): User defined data to pass to @link_func. * * Copy an item using the item class' "copy" method. Like ipatch_item_copy() * but takes a @link_func which can be used for handling replication of * externally linked objects (recursively deep duplicate for example, although * there is ipatch_item_duplicate_deep() specifically for that). */ void ipatch_item_copy_link_func(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchItemClass *klass; GType dest_type, src_type; g_return_if_fail(IPATCH_IS_ITEM(dest)); g_return_if_fail(IPATCH_IS_ITEM(src)); dest_type = G_OBJECT_TYPE(dest); src_type = G_OBJECT_TYPE(src); g_return_if_fail(g_type_is_a(dest_type, src_type)); klass = IPATCH_ITEM_GET_CLASS(src); g_return_if_fail(klass->copy != NULL); klass->copy(dest, src, link_func, user_data); } /** * ipatch_item_copy_replace: * @dest: New destination item to copy to (should be same type or derived * from @src type) * @src: Item to copy from * @repl_hash: (element-type IpatchItem IpatchItem): Hash of link pointer * substitutions. The original link pointer is the hash key, and the * hash value is the replacement pointer. * * Like ipatch_item_copy() but takes a link replacement hash, which can be * used for substituting different objects for object links. See * ipatch_item_copy_link_func() if even more flexibility is desired. */ void ipatch_item_copy_replace(IpatchItem *dest, IpatchItem *src, GHashTable *repl_hash) { IpatchItemClass *klass; GType dest_type, src_type; g_return_if_fail(IPATCH_IS_ITEM(dest)); g_return_if_fail(IPATCH_IS_ITEM(src)); dest_type = G_OBJECT_TYPE(dest); src_type = G_OBJECT_TYPE(src); g_return_if_fail(g_type_is_a(dest_type, src_type)); klass = IPATCH_ITEM_GET_CLASS(src); g_return_if_fail(klass->copy != NULL); klass->copy(dest, src, ipatch_item_copy_link_func_hash, repl_hash); } /** * ipatch_item_duplicate: * @item: Item to duplicate * * A convenience function to duplicate an item. Like ipatch_item_copy() but * creates a new duplicate item, rather than using an existing item. Note * that externally linked objects are not duplicated for non #IpatchBase * derived types, making the item local to the same #IpatchBase object. * * Returns: (transfer full): New duplicate item. Caller owns the reference to the new item. */ IpatchItem * ipatch_item_duplicate(IpatchItem *item) { IpatchItem *newitem; g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL); newitem = g_object_new(G_OBJECT_TYPE(item), NULL); /* ++ ref new item */ g_return_val_if_fail(newitem != NULL, NULL); ipatch_item_copy(newitem, item); return (newitem); /* !! caller owns reference */ } /** * ipatch_item_duplicate_link_func: * @item: Item to duplicate * @link_func: (scope call) (closure user_data): Function to call for each * object link pointer in @item. This function can alter the copied link if desired. * @user_data: (nullable): User defined data to pass to @link_func. * * Like ipatch_item_copy_link_func() but duplicates the item instead of * copying to an existing item. * * Returns: (transfer full): New duplicate item. Caller owns the reference to the new item. */ IpatchItem * ipatch_item_duplicate_link_func(IpatchItem *item, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchItem *newitem; g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL); newitem = g_object_new(G_OBJECT_TYPE(item), NULL); /* ++ ref new item */ g_return_val_if_fail(newitem != NULL, NULL); ipatch_item_copy_link_func(newitem, item, link_func, user_data); return (newitem); /* !! caller owns reference */ } /** * ipatch_item_duplicate_replace: * @item: Item to duplicate * @repl_hash: (element-type IpatchItem IpatchItem): Hash of link pointer * substitutions. The original link pointer is the hash key, and the * hash value is the replacement pointer. * * Like ipatch_item_copy_replace() but duplicates the item instead of * copying it to an already created item. * * Returns: (transfer full): New duplicate item. Caller owns the reference to the new item. */ IpatchItem * ipatch_item_duplicate_replace(IpatchItem *item, GHashTable *repl_hash) { IpatchItem *newitem; g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL); newitem = g_object_new(G_OBJECT_TYPE(item), NULL); /* ++ ref new item */ g_return_val_if_fail(newitem != NULL, NULL); ipatch_item_copy_replace(newitem, item, repl_hash); return (newitem); /* !! caller owns reference */ } /** * ipatch_item_duplicate_deep: * @item: Item to deep duplicate * * Recursively duplicate an item (i.e., its dependencies also). * * Returns: (transfer full): List of duplicated @item and dependencies * (duplicated @item is the first in the list). List object is owned by * the caller and should be unreferenced when finished using it. */ IpatchList * ipatch_item_duplicate_deep(IpatchItem *item) { IpatchItemClass *klass; IpatchItem *newitem; IpatchList *list; GHashTable *linkhash; /* link substitutions */ g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL); klass = IPATCH_ITEM_GET_CLASS(item); g_return_val_if_fail(klass->copy != NULL, NULL); newitem = g_object_new(G_OBJECT_TYPE(item), NULL); /* ++ ref new item */ g_return_val_if_fail(newitem != NULL, NULL); /* create hash to track new duplicated items */ linkhash = g_hash_table_new(NULL, NULL); g_hash_table_insert(linkhash, item, newitem); /* do the copy using the deep copy link function callback */ klass->copy(newitem, item, ipatch_item_copy_link_func_deep, linkhash); list = ipatch_list_new(); /* ++ref new list */ /* remove newitem, before convert to list */ g_hash_table_remove(linkhash, newitem); /* convert hash to IpatchList (!! list takes over references) */ g_hash_table_foreach(linkhash, copy_hash_to_list_GHFunc, list); /* prepend newitem (!! List takes over creator's reference) */ list->items = g_list_prepend(list->items, newitem); g_hash_table_destroy(linkhash); return (list); } /* GHFunc to convert hash table to IpatchList */ static void copy_hash_to_list_GHFunc(gpointer key, gpointer value, gpointer user_data) { IpatchList *list = (IpatchList *)user_data; list->items = g_list_prepend(list->items, value); } /** * ipatch_item_copy_link_func_deep: (skip) * @item: Item which is being linked (contains a reference to @link). * @link: The item being referenced (can be %NULL). * @user_data: User data supplied to copy/duplicate function. * * IpatchItemCopyLinkFunc for deep duplicating an object and dependencies. * * Returns: Pointer to item to use for link property (duplicated). */ IpatchItem * ipatch_item_copy_link_func_deep(IpatchItem *item, IpatchItem *link, gpointer user_data) { GHashTable *linkhash = (GHashTable *)user_data; IpatchItem *dup = NULL; if(!link) { return (NULL); } /* look up link item in duplicate hash. */ if(linkhash) { dup = g_hash_table_lookup(linkhash, link); } if(!dup) /* link not in hash? - Duplicate link and add it to hash. */ { dup = g_object_new(G_OBJECT_TYPE(link), NULL); /* ++ ref new item */ g_return_val_if_fail(dup != NULL, NULL); /* !! hash table takes over reference */ g_hash_table_insert(linkhash, link, dup); ipatch_item_copy_link_func(dup, link, ipatch_item_copy_link_func_deep, user_data); } return (dup); } /** * ipatch_item_copy_link_func_hash: (skip) * @item: Item which is being linked (contains a reference to @link). * @link: The item being referenced. * @user_data: (type GHashTable): User data supplied to copy/duplicate function. * * A predefined #IpatchItemCopyLinkFunc which takes a hash for the @user_data * component, which is used for replacing link pointers. @link is used as the * hash key, the hash value is used in its place if present otherwise @link is * used unchanged. * * Returns: Value in hash (@user_data) if @link is present in hash, or @link * itself if not. */ IpatchItem * ipatch_item_copy_link_func_hash(IpatchItem *item, IpatchItem *link, gpointer user_data) { GHashTable *hash = (GHashTable *)user_data; IpatchItem *repl = NULL; if(!link) { return (NULL); } if(hash) { repl = g_hash_table_lookup(hash, link); } return (repl ? repl : link); } /** * ipatch_item_type_can_conflict: * @item_type: Type to test * * Test if a given #IpatchItem derived type can conflict with another item * (only applies to items of the same type in the same #IpatchBase object). * * Returns: %TRUE if @item_type can conflict (has properties which should be * unique among its siblings), %FALSE otherwise. */ gboolean ipatch_item_type_can_conflict(GType item_type) { UniqueBag *unique; unique = item_lookup_unique_bag(item_type); return (unique != NULL); } /** * ipatch_item_type_get_unique_specs: * @item_type: Type to get unique parameter specs for * @groups: (out) (optional): Location to store an integer describing groups. * Each bit corresponds to a GParamSpec in the returned array. GParamSpecs of the * same group will have the same bit value (0 or 1) and be consecutive. * Unique properties in the same group must all match (logic AND) for a * conflict to occur. Pass %NULL to ignore. * * Get the list of unique parameter specs which can conflict for a given * item type. * * Returns: (array zero-terminated=1) (transfer none): %NULL terminated list of * parameter specs or %NULL if @item_type can never conflict. The returned * array is internal and should not be modified or freed. */ GParamSpec ** ipatch_item_type_get_unique_specs(GType item_type, guint32 *groups) { UniqueBag *unique; unique = item_lookup_unique_bag(item_type); if(unique) { if(groups) { *groups = unique->groups; } return (unique->pspecs); } else { if(groups) { *groups = 0; } return (NULL); } } /** * ipatch_item_get_unique_props: * @item: Item to get unique properties of * * Get the values of the unique properties for @item. This is useful when * doing conflict detection processing and more flexibility is desired than * with ipatch_item_test_conflict(). * * Returns: GValueArray of the unique property values of @item in the same * order as the parameter specs returned by ipatch_item_type_get_unique_specs(). * %NULL is returned if @item doesn't have any unique properties. * Use g_value_array_free() to free the array when finished using it. */ GValueArray * ipatch_item_get_unique_props(IpatchItem *item) { GParamSpec **ps; UniqueBag *unique; GValueArray *vals; GValue *value; int count, i; g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL); /* item has any unique properties? */ unique = item_lookup_unique_bag(G_OBJECT_TYPE(item)); if(!unique) { return (NULL); } /* count param specs */ for(count = 0, ps = unique->pspecs; *ps; count++, ps++); vals = g_value_array_new(count); for(i = 0, ps = unique->pspecs; i < count; i++, ps++) { /* NULL will append an unset value, saves a value copy operation */ g_value_array_append(vals, NULL); value = g_value_array_get_nth(vals, i); ipatch_item_get_property_fast(item, *ps, value); } return (vals); } /** * ipatch_item_test_conflict: * @item1: First item to test * @item2: Second item to test * * Test if two items would conflict if they were siblings (doesn't actually * test if they are siblings). * * Returns: Flags of unique properties which conflict. The bit index * corresponds to the parameter index in the array returned by * ipatch_item_type_get_unique_specs(). 0 is returned if the items do not * conflict. */ guint ipatch_item_test_conflict(IpatchItem *item1, IpatchItem *item2) { GValue val1 = { 0 }, val2 = { 0 }; UniqueBag *unique; GParamSpec *pspec; GType type; guint conflicts; guint mask; int i, count, toggle, groupcount; g_return_val_if_fail(IPATCH_IS_ITEM(item1), 0); g_return_val_if_fail(IPATCH_IS_ITEM(item2), 0); type = G_OBJECT_TYPE(item1); /* can't conflict if items not the same type */ if(type != G_OBJECT_TYPE(item2)) { return (0); } unique = item_lookup_unique_bag(type); if(!unique) { return (0); /* no unique properties? */ } conflicts = 0; for(i = 0; TRUE; i++) { pspec = unique->pspecs[i]; if(!pspec) { break; } ipatch_item_get_property_fast(item1, pspec, &val1); ipatch_item_get_property_fast(item2, pspec, &val2); if(g_param_values_cmp(pspec, &val1, &val2) == 0) { conflicts |= (1 << i); } g_value_unset(&val1); g_value_unset(&val2); } /* mask out any unique groups which don't have all its props conflicting */ count = i; mask = 1; groupcount = 1; toggle = (unique->groups & 1); /* to track group changes */ for(i = 1; i < count; i++) { /* same group as prev. property? */ if(toggle == ((unique->groups & (1 << i)) != 0)) { mask |= (1 << i); /* add another mask bit */ groupcount++; /* increment group property count */ } else /* group changed */ { /* prev group is larger than 1 prop. and not all props conflicting? */ if(groupcount > 1 && (conflicts & mask) != mask) { conflicts &= ~mask; /* clear all conflicts for prev group */ } toggle ^= 1; mask = (1 << i); groupcount = 1; } } /* check if last gorup is larger than 1 prop. and not all props conflicting */ if(groupcount > 1 && (conflicts & mask) != mask) { conflicts &= ~mask; /* clear all conflicts for last group */ } return (conflicts); } /* lookup cached unique property info for a given type */ static UniqueBag * item_lookup_unique_bag(GType type) { GParamSpec **pspecs, *ps; GList *speclist = NULL; /* sorted spec list (sorted by unique group) */ UniqueBag *unique; gpointer klass; guint n_props, count; guint i; G_LOCK(unique_prop_cache); /* has not been cached yet? */ if(! g_hash_table_lookup_extended(unique_prop_cache, GUINT_TO_POINTER (type), NULL, (gpointer *)&unique)) { klass = g_type_class_ref(type); /* ++ref */ g_return_val_if_fail(klass != NULL, NULL); /* get property list and add unique properties to speclist */ pspecs = g_object_class_list_properties(klass, &n_props); /* alloc */ for(i = 0, count = 0; i < n_props; i++) { ps = pspecs[i]; if(ps->flags & IPATCH_PARAM_UNIQUE) /* unique property? */ { /* add to speclist sorted by unique group (0 = nogroup) */ speclist = g_list_insert_sorted(speclist, ps, unique_group_list_sort_func); count++; } } g_free(pspecs); /* free */ g_type_class_unref(klass); /* --ref */ if(speclist) /* any unique properties? */ { guint group, lastgroup = 0; int toggle = 0; GList *p; /* create unique bag */ unique = g_new(UniqueBag, 1); unique->pspecs = g_new(GParamSpec *, count + 1); unique->groups = 0; /* add pointers to unique spec array and set group bit toggle field */ for(i = 0, p = speclist; i < count; i++, p = p->next) { unique->pspecs[i] = (GParamSpec *)(p->data); ipatch_param_get((GParamSpec *)(p->data), "unique-group-id", &group, NULL); if(group != lastgroup) { toggle ^= 1; } if(toggle) { unique->groups |= (1 << i); } lastgroup = group; } unique->pspecs[count] = NULL; /* NULL terminated */ } else { unique = NULL; /* indicate no unique properties */ } g_hash_table_insert(unique_prop_cache, GUINT_TO_POINTER(type), unique); } G_UNLOCK(unique_prop_cache); return (unique); } /* sort GParamSpec pointers by their unique group id (0 = no group) */ static gint unique_group_list_sort_func(gconstpointer a, gconstpointer b) { GParamSpec *aspec = (GParamSpec *)a, *bspec = (GParamSpec *)b; int agroup, bgroup; ipatch_param_get(aspec, "unique-group-id", &agroup, NULL); ipatch_param_get(bspec, "unique-group-id", &bgroup, NULL); return (agroup - bgroup); } /** * ipatch_item_set_atomic: * @item: (type IpatchItem): IpatchItem derived object to set properties of * @first_property_name: Name of first property * @...: Variable list of arguments that should start with the value to * set @first_property_name to, followed by property name/value pairs. List is * terminated with a %NULL property name. * * Sets properties on a patch item atomically (i.e. item is * multi-thread locked while all properties are set). This avoids * critical parameter sync problems when multiple threads are * accessing the same item. See g_object_set() for more information on * setting properties. */ void ipatch_item_set_atomic(gpointer item, const char *first_property_name, ...) { va_list args; g_return_if_fail(IPATCH_IS_ITEM(item)); va_start(args, first_property_name); IPATCH_ITEM_WLOCK(item); g_object_set_valist(G_OBJECT(item), first_property_name, args); IPATCH_ITEM_WUNLOCK(item); va_end(args); } /** * ipatch_item_get_atomic: * @item: (type IpatchItem): IpatchItem derived object to get properties from * @first_property_name: Name of first property * @...: Variable list of arguments that should start with a * pointer to store the value from @first_property_name, followed by * property name/value pointer pairs. List is terminated with a %NULL * property name. * * Gets properties from a patch item atomically (i.e. item is * multi-thread locked while all properties are retrieved). This * avoids critical parameter sync problems when multiple threads are * accessing the same item. See g_object_get() for more information on * getting properties. */ void ipatch_item_get_atomic(gpointer item, const char *first_property_name, ...) { va_list args; g_return_if_fail(IPATCH_IS_ITEM(item)); va_start(args, first_property_name); IPATCH_ITEM_WLOCK(item); g_object_get_valist(G_OBJECT(item), first_property_name, args); IPATCH_ITEM_WUNLOCK(item); va_end(args); } /** * ipatch_item_first: (skip) * @iter: Patch item iterator containing #IpatchItem items * * Gets the first item in a patch item iterator. A convenience wrapper for * ipatch_iter_first(). * * Returns: The first item in @iter or %NULL if empty. */ IpatchItem * ipatch_item_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_ITEM(obj)); } else { return (NULL); } } /** * ipatch_item_next: (skip) * @iter: Patch item iterator containing #IpatchItem items * * Gets the next item in a patch item iterator. A convenience wrapper for * ipatch_iter_next(). * * Returns: The next item in @iter or %NULL if no more items. */ IpatchItem * ipatch_item_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_ITEM(obj)); } else { return (NULL); } } libinstpatch-1.1.6/libinstpatch/IpatchItem.h000066400000000000000000000257671400263525300211100ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_ITEM_H__ #define __IPATCH_ITEM_H__ #include #include #include typedef struct _IpatchItem IpatchItem; typedef struct _IpatchItemClass IpatchItemClass; #include #include #define IPATCH_TYPE_ITEM (ipatch_item_get_type ()) #define IPATCH_ITEM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_ITEM, IpatchItem)) #define IPATCH_ITEM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_ITEM, IpatchItemClass)) #define IPATCH_IS_ITEM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_ITEM)) #define IPATCH_IS_ITEM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_ITEM)) #define IPATCH_ITEM_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_ITEM, IpatchItemClass)) /** * IpatchItemCopyLinkFunc: * @item: Item which is being linked (contains a reference to @link). * @link: The item being referenced (can be NULL). * @user_data: User data supplied to copy/duplicate function. * * A callback function called during item copy/duplicate operations for * any item link reference which needs to be resolved. An example usage * is for deep duplicating an object (all references can also be duplicated). * Item copy methods should call this for any linked item references which are * not part of the new object. * * Returns: Pointer to item to use for link property (can be the @link item * if the duplicated/copied item is local to the same file). */ typedef IpatchItem *(*IpatchItemCopyLinkFunc)(IpatchItem *item, IpatchItem *link, gpointer user_data); /* Base patch item object */ struct _IpatchItem { GObject object; /*< private >*/ int flags; /* flag field (atomic int ops used) */ IpatchItem *parent; /* parent item or NULL if not parented or root */ IpatchItem *base; /* base parent object or NULL */ GStaticRecMutex *mutex; /* pointer to mutex (could be a parent's mutex) */ }; /* Base patch item class */ struct _IpatchItemClass { GObjectClass parent_class; /*< public >*/ gboolean mutex_slave; /* set to TRUE to use parent thread mutex */ /* methods */ void (*item_set_property)(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); void (*copy)(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); void (*remove)(IpatchItem *item); /** * remove_full: * @item: Item to remove references to/from * @full: %TRUE removes all references to and from @item, %FALSE removes only references to @item * * Removes references to/from (depending on the value of @full) @item of other objects in the same * #IpatchBase object. The remove method can be used instead for default behavior of removing item's * children if it is a container and @full is TRUE. * * Since: 1.1.0 */ void (*remove_full)(IpatchItem *item, gboolean full); }; typedef enum { IPATCH_ITEM_HOOKS_ACTIVE = 1 << 0, /* hook callbacks active? */ IPATCH_ITEM_FREE_MUTEX = 1 << 1 /* TRUE if item should free its mutex */ } IpatchItemFlags; /** * IPATCH_ITEM_UNUSED_FLAG_SHIFT: (skip) */ /* 2 flags + reserve 2 bits for future expansion */ #define IPATCH_ITEM_UNUSED_FLAG_SHIFT 4 /* Multi-thread locking macros. For now there is no distinction between write and read locking since GStaticRWLock is not recursive. */ #define IPATCH_ITEM_WLOCK(item) \ g_static_rec_mutex_lock (((IpatchItem *)(item))->mutex) #define IPATCH_ITEM_WUNLOCK(item) \ g_static_rec_mutex_unlock (((IpatchItem *)(item))->mutex) #define IPATCH_ITEM_RLOCK(item) IPATCH_ITEM_WLOCK(item) #define IPATCH_ITEM_RUNLOCK(item) IPATCH_ITEM_WUNLOCK(item) /** * IpatchItemPropNotify: * @item: Item whose property changed * @pspec: Parameter spec of property which changed * @new_value: New value assigned to the property * @old_value: Old value that the property had (can be %NULL for read only * properties) * @user_data: User defined pointer from when callback was added * @event_ptrs: Per event data defined by users of callback system * * Property notify information structure. */ typedef struct { IpatchItem *item; /* item whose property changed */ GParamSpec *pspec; /* property spec of the property that changed */ const GValue *new_value; /* new value of the property */ const GValue *old_value; /* old value of the property (can be NULL!) */ gpointer user_data; /* user defined data, defined on connect */ /* per event data */ struct { gpointer data; /* implementation defined data per event */ GDestroyNotify destroy; /* function called to cleanup for @data */ } eventdata[4]; } IpatchItemPropNotify; /** * IPATCH_ITEM_PROP_NOTIFY_SET_EVENT: * @info: IpatchItemPropNotify pointer * @index: Pointer index (0-3) * @data: Data to assign to pointer field * @destroy: Callback function to cleanup @data when done (or %NULL) * * A macro for assigning per event pointers to #IpatchItemPropNotify. */ #define IPATCH_ITEM_PROP_NOTIFY_SET_EVENT(info, index, data, destroy) \ (info)->eventdata[index].data = data; (info)->eventdata[index].destroy = destroy /** * IpatchItemPropCallback: * * IpatchItem property change callback function prototype. */ typedef void (*IpatchItemPropCallback)(IpatchItemPropNotify *notify); /** * IpatchItemPropDisconnect: * @item: Item of prop change match criteria * @pspec: Parameter spec of prop change match criteria * @user_data: User defined pointer from when callback was added * * Function prototype which gets called when a property notify callback gets * disconnected. */ typedef void (*IpatchItemPropDisconnect)(IpatchItem *item, GParamSpec *pspec, gpointer user_data); /* Convenience macro used by IpatchItem copy methods to call a copy link function, or use the link pointer directly if function is NULL */ #define IPATCH_ITEM_COPY_LINK_FUNC(item, link, func, userdata) \ (func ? func (item, link, userdata) : link) /* stored publicy for added convenience to derived types */ /* Useful when libinstpatch library is used as a static library. */ extern GParamSpec *ipatch_item_pspec_title; /* Getter function returning title property GParamSpec as a convenience to derived type. Useful when libinstpatch library is used as a shared library linked at load time. */ GParamSpec *ipatch_item_get_pspec_title(void); GType ipatch_item_get_type(void); int ipatch_item_get_flags(gpointer item); void ipatch_item_set_flags(gpointer item, int flags); void ipatch_item_clear_flags(gpointer item, int flags); void ipatch_item_set_parent(IpatchItem *item, IpatchItem *parent); void ipatch_item_unparent(IpatchItem *item); IpatchItem *ipatch_item_get_parent(IpatchItem *item); IpatchItem *ipatch_item_peek_parent(IpatchItem *item); IpatchItem *ipatch_item_get_base(IpatchItem *item); IpatchItem *ipatch_item_peek_base(IpatchItem *item); IpatchItem *ipatch_item_get_ancestor_by_type(IpatchItem *item, GType ancestor_type); IpatchItem *ipatch_item_peek_ancestor_by_type(IpatchItem *item, GType ancestor_type); void ipatch_item_remove(IpatchItem *item); void ipatch_item_remove_full(IpatchItem *item, gboolean full); void ipatch_item_remove_recursive(IpatchItem *item, gboolean full); void ipatch_item_changed(IpatchItem *item); void ipatch_item_get_property_fast(IpatchItem *item, GParamSpec *pspec, GValue *value); void ipatch_item_copy(IpatchItem *dest, IpatchItem *src); void ipatch_item_copy_link_func(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); void ipatch_item_copy_replace(IpatchItem *dest, IpatchItem *src, GHashTable *repl_hash); IpatchItem *ipatch_item_duplicate(IpatchItem *item); IpatchItem *ipatch_item_duplicate_link_func(IpatchItem *item, IpatchItemCopyLinkFunc link_func, gpointer user_data); IpatchItem *ipatch_item_duplicate_replace(IpatchItem *item, GHashTable *repl_hash); IpatchList *ipatch_item_duplicate_deep(IpatchItem *item); IpatchItem *ipatch_item_copy_link_func_deep(IpatchItem *item, IpatchItem *link, gpointer user_data); IpatchItem *ipatch_item_copy_link_func_hash(IpatchItem *item, IpatchItem *link, gpointer user_data); gboolean ipatch_item_type_can_conflict(GType item_type); GParamSpec **ipatch_item_type_get_unique_specs(GType item_type, guint32 *groups); GValueArray *ipatch_item_get_unique_props(IpatchItem *item); guint ipatch_item_test_conflict(IpatchItem *item1, IpatchItem *item2); void ipatch_item_set_atomic(gpointer item, const char *first_property_name, ...); void ipatch_item_get_atomic(gpointer item, const char *first_property_name, ...); IpatchItem *ipatch_item_first(IpatchIter *iter); IpatchItem *ipatch_item_next(IpatchIter *iter); /* in IpatchItemProp.c */ void ipatch_item_prop_notify(IpatchItem *item, GParamSpec *pspec, const GValue *new_value, const GValue *old_value); void ipatch_item_prop_notify_by_name(IpatchItem *item, const char *prop_name, const GValue *new_value, const GValue *old_value); guint ipatch_item_prop_connect(IpatchItem *item, GParamSpec *pspec, IpatchItemPropCallback callback, IpatchItemPropDisconnect disconnect, gpointer user_data); guint ipatch_item_prop_connect_by_name(IpatchItem *item, const char *prop_name, IpatchItemPropCallback callback, IpatchItemPropDisconnect disconnect, gpointer user_data); void ipatch_item_prop_disconnect(guint handler_id); void ipatch_item_prop_disconnect_matched(IpatchItem *item, GParamSpec *pspec, IpatchItemPropCallback callback, gpointer user_data); void ipatch_item_prop_disconnect_by_name(IpatchItem *item, const char *prop_name, IpatchItemPropCallback callback, gpointer user_data); #endif libinstpatch-1.1.6/libinstpatch/IpatchItemProp.c000066400000000000000000000643601400263525300217340ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /* * IpatchItemProp.c - IpatchItem property change callback system */ #include #include #include #include "IpatchItem.h" #include "IpatchParamProp.h" #include "ipatch_priv.h" /* hash key for IpatchItem property matching */ typedef struct { IpatchItem *item; /* IpatchItem to match for callback */ GParamSpec *pspec; /* GParamSpec to match for callback */ } PropMatchKey; /* hash value used for IpatchItem property callbacks */ typedef struct { IpatchItemPropCallback callback; /* callback function */ IpatchItemPropDisconnect disconnect; /* called when callback is disconnected */ GDestroyNotify notify_func; /* Either notify_func or disconnect will be set, but not both */ gpointer user_data; /* user data to pass to function */ guint handler_id; /* unique handler ID */ } PropCallback; static void _ipatch_item_prop_free_prop_callback(PropCallback *cb); static void _free_gslist_prop_callback(GSList *list); static gboolean _ipatch_item_prop_free_hash_value(gpointer key, gpointer value, gpointer user_data); static void prop_match_key_free(gpointer data); static guint prop_callback_hash_func(gconstpointer key); static gboolean prop_callback_equal_func(gconstpointer a, gconstpointer b); static guint ipatch_item_prop_real_connect(IpatchItem *item, GParamSpec *pspec, IpatchItemPropCallback callback, IpatchItemPropDisconnect disconnect, GDestroyNotify notify_func, gpointer user_data); static void ipatch_item_prop_real_disconnect(guint handler_id, IpatchItem *item, GParamSpec *pspec, IpatchItemPropCallback callback, gpointer user_data); static gboolean prop_callback_hash_GHRFunc(gpointer key, gpointer value, gpointer user_data); /* lock for prop_callback_next_id, prop_callback_hash and wild_prop_callback_list */ G_LOCK_DEFINE_STATIC(prop_callbacks); static guint prop_callback_next_id = 1; /* next handler ID */ /* hash of IpatchItem prop callbacks (PropMatchKey -> GSList) */ static GHashTable *prop_callback_hash; /* wildcard callbacks (item and property are wildcard) */ static GSList *wild_prop_callback_list = NULL; /* Initialization/deinitialization of 'IpatchItem property change callback' system */ /* initialization system */ void _ipatch_item_prop_init (void) { /* next handler ID */ prop_callback_next_id = 1; /* create IpatchItem property callback hash */ prop_callback_hash = g_hash_table_new_full(prop_callback_hash_func, prop_callback_equal_func, prop_match_key_free, NULL); /* wildcard callbacks (item and property are wildcard) */ wild_prop_callback_list = NULL; } /* Freeing system */ void _ipatch_item_prop_deinit(void) { /* free prop_callback_hash */ g_hash_table_foreach_remove (prop_callback_hash, _ipatch_item_prop_free_hash_value, NULL); g_hash_table_destroy(prop_callback_hash); /* free wild_prop_callback_list */ _free_gslist_prop_callback(wild_prop_callback_list); } /* free hash entry */ static gboolean _ipatch_item_prop_free_hash_value(gpointer key, gpointer value, gpointer user_data) { _free_gslist_prop_callback((GSList *)value); return TRUE; } static void _free_gslist_prop_callback(GSList *list) { g_slist_free_full(list, (GDestroyNotify)_ipatch_item_prop_free_prop_callback); } static void _ipatch_item_prop_free_prop_callback(PropCallback *cb) { g_slice_free(PropCallback, cb); } /*--- API of 'IpatchItem property change callback' system -------------------*/ /* GDestroyNotify function to free prop_callback_hash keys */ static void prop_match_key_free(gpointer data) { g_slice_free(PropMatchKey, data); } /* hash function for IpatchItem property notify callback hash */ static guint prop_callback_hash_func(gconstpointer key) { const PropMatchKey *propkey = (const PropMatchKey *)key; return (GPOINTER_TO_UINT(propkey->item) + GPOINTER_TO_UINT(propkey->pspec)); } /* hash equal function for IpatchItem property notify callback hash */ static gboolean prop_callback_equal_func(gconstpointer a, gconstpointer b) { const PropMatchKey *akey = (const PropMatchKey *)a; const PropMatchKey *bkey = (const PropMatchKey *)b; return (akey->item == bkey->item && akey->pspec == bkey->pspec); } /** * ipatch_item_prop_notify: * @item: Item whose property changed * @pspec: Parameter specification for @item of parameter that changed * @new_value: The new value that was assigned * @old_value: (nullable): Old value that property had (can be %NULL for read only props) * * Usually only used by #IpatchItem object implementations, rather * than explicitly called by the user. It should be called AFTER item * property changes that occur outside of the #IpatchItem * item_set_property method. */ void ipatch_item_prop_notify(IpatchItem *item, GParamSpec *pspec, const GValue *new_value, const GValue *old_value) { /* dynamically adjustable max callbacks to allocate cbarray for */ static guint max_callbacks = 64; PropMatchKey cbkey; PropCallback *cbarray; /* NULL terminated array */ PropCallback *cb; PropCallback *old_cbarray; guint old_max_callbacks; GSList *match_both, *match_item, *match_prop, *wild_list; guint cbindex = 0, i; g_return_if_fail(IPATCH_IS_ITEM(item)); g_return_if_fail(G_IS_PARAM_SPEC(pspec)); g_return_if_fail(G_IS_VALUE(new_value)); g_return_if_fail(!old_value || G_IS_VALUE(old_value)); /* should save-able state change be indicated (set #IpatchBase dirty flag) */ if(!(pspec->flags & IPATCH_PARAM_NO_SAVE_CHANGE)) { ipatch_item_changed(item); } /* if hooks not active for item, just return */ if(!(ipatch_item_get_flags(item) & IPATCH_ITEM_HOOKS_ACTIVE)) { return; } /* key for IpatchItem property callback hash */ cbkey.item = item; cbkey.pspec = pspec; /* allocate callback array on stack (for performance) */ cbarray = g_alloca(max_callbacks * sizeof(PropCallback)); G_LOCK(prop_callbacks); /* lookup callback list for this IpatchItem:Property */ match_both = g_hash_table_lookup(prop_callback_hash, &cbkey); /* lookup callback list for IpatchItem:* */ cbkey.pspec = NULL; match_item = g_hash_table_lookup(prop_callback_hash, &cbkey); /* lookup callback list for *:Property */ cbkey.pspec = pspec; cbkey.item = NULL; match_prop = g_hash_table_lookup(prop_callback_hash, &cbkey); wild_list = wild_prop_callback_list; /* duplicate lists into array (since we will call them outside of lock) */ for(; match_both && cbindex < max_callbacks; match_both = match_both->next) { cbarray[cbindex++] = *(PropCallback *)(match_both->data); } for(; match_item && cbindex < max_callbacks; match_item = match_item->next) { cbarray[cbindex++] = *(PropCallback *)(match_item->data); } for(; match_prop && cbindex < max_callbacks; match_prop = match_prop->next) { cbarray[cbindex++] = *(PropCallback *)(match_prop->data); } for(; wild_list && cbindex < max_callbacks; wild_list = wild_list->next) { cbarray[cbindex++] = *(PropCallback *)(wild_list->data); } if(match_both || match_item || match_prop || wild_list) { /* We exceeded max_callbacks (Bender just shit a brick) */ old_cbarray = cbarray; old_max_callbacks = max_callbacks; cbindex += g_slist_length(match_both); cbindex += g_slist_length(match_item); cbindex += g_slist_length(match_prop); cbindex += g_slist_length(wild_list); max_callbacks = cbindex + 16; /* plus some for kicks */ cbarray = g_alloca(max_callbacks * sizeof(PropCallback)); memcpy(cbarray, old_cbarray, old_max_callbacks * sizeof(PropCallback)); cbindex = old_max_callbacks; /* duplicate rest of the lists */ for(; match_both && cbindex < max_callbacks; match_both = match_both->next) { cbarray[cbindex++] = *(PropCallback *)(match_both->data); } for(; match_item && cbindex < max_callbacks; match_item = match_item->next) { cbarray[cbindex++] = *(PropCallback *)(match_item->data); } for(; match_prop && cbindex < max_callbacks; match_prop = match_prop->next) { cbarray[cbindex++] = *(PropCallback *)(match_prop->data); } for(; wild_list && cbindex < max_callbacks; wild_list = wild_list->next) { cbarray[cbindex++] = *(PropCallback *)(wild_list->data); } } G_UNLOCK(prop_callbacks); if(cbindex) { IpatchItemPropNotify info = { 0 }; info.item = item; info.pspec = pspec; info.new_value = new_value; info.old_value = old_value; /* call the callbacks */ for(i = 0; i < cbindex; i++) { cb = &cbarray[i]; info.user_data = cb->user_data; cb->callback(&info); } /* call event data destroy functions if any have been set */ if(info.eventdata[0].destroy) { info.eventdata[0].destroy(info.eventdata[0].data); } if(info.eventdata[1].destroy) { info.eventdata[1].destroy(info.eventdata[1].data); } if(info.eventdata[2].destroy) { info.eventdata[2].destroy(info.eventdata[2].data); } if(info.eventdata[3].destroy) { info.eventdata[3].destroy(info.eventdata[3].data); } } } /** * ipatch_item_prop_notify_by_name: * @item: Item whose property changed * @prop_name: Name of property that changed * @new_value: The new value that was assigned * @old_value: (nullable): Old value that property had (can be %NULL * for read only properties) * * Usually only used by #IpatchItem object implementations, rather * than explicitly called by the user. Like ipatch_item_prop_notify() * except takes a property name instead of a parameter spec, for * added convenience. */ void ipatch_item_prop_notify_by_name(IpatchItem *item, const char *prop_name, const GValue *new_value, const GValue *old_value) { GObjectClass *klass; GParamSpec *pspec, *redirect; g_return_if_fail(IPATCH_IS_ITEM(item)); g_return_if_fail(prop_name != NULL); g_return_if_fail(G_IS_VALUE(new_value)); g_return_if_fail(!old_value || G_IS_VALUE(old_value)); /* get the item's class and lookup the property */ klass = G_OBJECT_GET_CLASS(item); pspec = g_object_class_find_property(klass, prop_name); g_return_if_fail(pspec != NULL); /* use overridden param spec if its an override param spec */ redirect = g_param_spec_get_redirect_target(pspec); if(redirect) { pspec = redirect; } ipatch_item_prop_notify(item, pspec, new_value, old_value); } /** * ipatch_item_prop_connect: (skip) * @item: (nullable): IpatchItem to match (or %NULL for wildcard) * @pspec: (nullable): Property parameter specification to match (or %NULL for wildcard) * @callback: (scope notified): Callback function * @disconnect: (nullable) (closure user_data): Callback disconnect function * (called when the callback is disconnected) can be %NULL. * @user_data: (nullable): User defined data to pass to @callback and @disconnect function. * * Connect a callback for a specific #IpatchItem and property. If a property * change occurs for the given @item and @pspec then the callback is * invoked. The parameters @item and/or @pspec may be %NULL for wild card * matching (if both are %NULL then callback will be called for all #IpatchItem * property changes). * * Returns: Unique handler ID which can be used to remove the handler or 0 on * error (only when function params are incorrect). */ guint ipatch_item_prop_connect(IpatchItem *item, GParamSpec *pspec, IpatchItemPropCallback callback, IpatchItemPropDisconnect disconnect, gpointer user_data) { return (ipatch_item_prop_real_connect(item, pspec, callback, disconnect, NULL, user_data)); } /** * ipatch_item_prop_connect_notify: (rename-to ipatch_item_prop_connect) * @item: (nullable): IpatchItem to match (or %NULL for wildcard) * @pspec: (nullable): Property parameter specification to match (or %NULL for wildcard) * @callback: (scope notified): Callback function * @notify_func: (scope async) (closure user_data) (nullable): Callback destroy notify * (called when the callback is disconnected) can be %NULL. * @user_data: (nullable): User defined data to pass to @callback and @disconnect function. * * Connect a callback for a specific #IpatchItem and property. If a property * change occurs for the given @item and @pspec then the callback is * invoked. The parameters @item and/or @pspec may be %NULL for wild card * matching (if both are %NULL then callback will be called for all #IpatchItem * property changes). * * Returns: Unique handler ID which can be used to remove the handler or 0 on * error (only when function params are incorrect). * * Since: 1.1.0 */ guint ipatch_item_prop_connect_notify(IpatchItem *item, GParamSpec *pspec, IpatchItemPropCallback callback, GDestroyNotify notify_func, gpointer user_data) { return (ipatch_item_prop_real_connect(item, pspec, callback, NULL, notify_func, user_data)); } static guint ipatch_item_prop_real_connect(IpatchItem *item, GParamSpec *pspec, IpatchItemPropCallback callback, IpatchItemPropDisconnect disconnect, GDestroyNotify notify_func, gpointer user_data) { PropMatchKey *cbkey = NULL; PropCallback *cb; GSList *cblist; guint handler_id; g_return_val_if_fail(!item || IPATCH_IS_ITEM(item), 0); g_return_val_if_fail(!pspec || G_IS_PARAM_SPEC(pspec), 0); g_return_val_if_fail(callback != NULL, 0); if(item || pspec) { cbkey = g_slice_new(PropMatchKey); cbkey->item = item; cbkey->pspec = pspec; } cb = g_slice_new(PropCallback); cb->callback = callback; cb->disconnect = disconnect; cb->notify_func = notify_func; cb->user_data = user_data; G_LOCK(prop_callbacks); handler_id = prop_callback_next_id++; cb->handler_id = handler_id; if(cbkey) { /* get existing list for IpatchItem:Property (if any) */ cblist = g_hash_table_lookup(prop_callback_hash, cbkey); /* if IpatchItem:Property already exists then cbkey gets freed here */ g_hash_table_insert(prop_callback_hash, cbkey, g_slist_prepend(cblist, cb)); } else /* callback is completely wildcard, just add to the wildcard list */ { wild_prop_callback_list = g_slist_prepend(wild_prop_callback_list, cb); } G_UNLOCK(prop_callbacks); return (handler_id); } /** * ipatch_item_prop_connect_by_name: (skip) * @item: (nullable): IpatchItem to match (or %NULL for wildcard) * @prop_name: Name of property of @item to match * @callback: Callback function * @disconnect: (nullable): Callback disconnect function (called when the callback is * disconnect) can be NULL. * @user_data: (closure): User defined data to pass to @callback and @disconnect function. * * Like ipatch_item_prop_add_callback() but takes the name of a property * instead of the parameter spec, for added convenience. * * Returns: Unique handler ID which can be used to remove the handler or 0 on * error (only when function params are incorrect). */ guint ipatch_item_prop_connect_by_name(IpatchItem *item, const char *prop_name, IpatchItemPropCallback callback, IpatchItemPropDisconnect disconnect, gpointer user_data) { GParamSpec *pspec; g_return_val_if_fail(IPATCH_IS_ITEM(item), 0); g_return_val_if_fail(prop_name != NULL, 0); pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(item), prop_name); g_return_val_if_fail(pspec != NULL, 0); return (ipatch_item_prop_real_connect(item, pspec, callback, disconnect, NULL, user_data)); } /** * ipatch_item_prop_connect_by_name_notify: (rename-to ipatch_item_prop_connect_by_name) * @item: (nullable): IpatchItem to match (or %NULL for wildcard) * @prop_name: Name of property of @item to match * @callback: Callback function * @notify_func: (scope async) (closure user_data) (nullable): Callback destroy notify * (called when the callback is disconnected) can be %NULL. * @user_data: (nullable): User defined data to pass to @callback and @disconnect function. * * Like ipatch_item_prop_add_callback() but takes the name of a property * instead of the parameter spec, for added convenience. * * Returns: Unique handler ID which can be used to remove the handler or 0 on * error (only when function params are incorrect). * * Since: 1.1.0 */ guint ipatch_item_prop_connect_by_name_notify(IpatchItem *item, const char *prop_name, IpatchItemPropCallback callback, GDestroyNotify notify_func, gpointer user_data) { GParamSpec *pspec; g_return_val_if_fail(IPATCH_IS_ITEM(item), 0); g_return_val_if_fail(prop_name != NULL, 0); pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(item), prop_name); g_return_val_if_fail(pspec != NULL, 0); return (ipatch_item_prop_real_connect(item, pspec, callback, NULL, notify_func, user_data)); } /* a bag used in next 2 removal functions */ typedef struct { gboolean found; /* Set to TRUE if handler found */ PropMatchKey key; /* out: (remove_callback func), in: (matched func) */ PropCallback cb; /* in: handler_id, out: disconnect, user_data */ PropMatchKey update_key; /* out: key of list root requiring update */ GSList *update_list; /* out: new root of list to update */ gboolean update_needed; /* out: set if root of list needs update */ } PropRemoveBag; /** * ipatch_item_prop_disconnect: * @handler_id: Handler ID as returned from ipatch_item_prop_connect(). * * Disconnects an #IpatchItem property change callback handler by its ID. */ void ipatch_item_prop_disconnect(guint handler_id) { g_return_if_fail(handler_id != 0); ipatch_item_prop_real_disconnect(handler_id, NULL, NULL, NULL, NULL); } /** * ipatch_item_prop_disconnect_matched: (skip) * @item: (nullable): #IpatchItem of handler to match (does not need to be valid) * @pspec: (nullable): GParamSpec of handler to match (does not need to be valid) * @callback: Callback function to match * @user_data: (nullable): User data to match * * Disconnects first #IpatchItem property change callback matching all the * function parameters. * Note: Only the pointer values of @item and @pspec are used so they don't * actually need to be valid anymore. */ void ipatch_item_prop_disconnect_matched(IpatchItem *item, GParamSpec *pspec, IpatchItemPropCallback callback, gpointer user_data) { g_return_if_fail(callback != NULL); ipatch_item_prop_real_disconnect(0, item, pspec, callback, user_data); } /** * ipatch_item_prop_disconnect_by_name: (skip) * @item: #IpatchItem of handler to match (NOTE: Must be a valid object!) * @prop_name: Name of property of @item to match * @callback: (nullable): Callback function to match * @user_data: (nullable): User data to match * * Like ipatch_item_prop_disconnect_matched() but takes a name of the * property to match instead of a parameter spec, for added convenience. * Note: @item must still be valid (to look up the property), contrary to * ipatch_item_prop_disconnect_matched(). */ void ipatch_item_prop_disconnect_by_name(IpatchItem *item, const char *prop_name, IpatchItemPropCallback callback, gpointer user_data) { GParamSpec *pspec; g_return_if_fail(IPATCH_IS_ITEM(item)); g_return_if_fail(prop_name != NULL); pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(item), prop_name); g_return_if_fail(pspec != NULL); ipatch_item_prop_disconnect_matched(item, pspec, callback, user_data); } /* used by ipatch_item_prop_disconnect and ipatch_item_prop_disconnect_matched. * Either handler_id should be set to a non-zero value or the other * parameters should be assigned but not both. */ static void ipatch_item_prop_real_disconnect(guint handler_id, IpatchItem *item, GParamSpec *pspec, IpatchItemPropCallback callback, gpointer user_data) { PropMatchKey *update_key; PropRemoveBag bag = { 0 }; PropCallback *cb; GSList *p; g_return_if_fail(handler_id != 0 || callback != NULL); g_return_if_fail(handler_id == 0 || callback == NULL); if(!handler_id) /* find by match criteria? */ { bag.key.item = item; bag.key.pspec = pspec; bag.cb.callback = callback; bag.cb.user_data = user_data; } else { bag.cb.handler_id = handler_id; /* find by handler ID */ } G_LOCK(prop_callbacks); /* only look in hash if ID search or item or pspec specified (not wildcard) */ if(handler_id || (item || pspec)) g_hash_table_foreach_remove(prop_callback_hash, prop_callback_hash_GHRFunc, &bag); /* hash entry did not get removed and requires list root to be updated, this can't be done in the GHRFunc */ if(bag.update_needed) { /* allocate and copy key for insert (gets freed) */ update_key = g_slice_new(PropMatchKey); *update_key = bag.update_key; g_hash_table_insert(prop_callback_hash, update_key, bag.update_list); } /* if not found and find ID or !item and !pspec (wildcard), check wildcard list */ if(!bag.found && (handler_id || (!item && !pspec))) { for(p = wild_prop_callback_list; p; p = p->next) { cb = (PropCallback *)(p->data); /* callback matches criteria? (handler_id or callback:user_data) */ if((handler_id && cb->handler_id == handler_id) || (!handler_id && cb->callback == callback && cb->user_data == user_data)) { bag.found = TRUE; bag.cb.disconnect = cb->disconnect; bag.cb.notify_func = cb->notify_func; bag.cb.user_data = cb->user_data; g_slice_free(PropCallback, cb); wild_prop_callback_list = g_slist_delete_link(wild_prop_callback_list, p); break; } } } G_UNLOCK(prop_callbacks); if(!bag.found) { if(handler_id) { g_critical(G_STRLOC ": Failed to find handler with ID '%d'", handler_id); } else g_critical(G_STRLOC ": Failed to find handler matching criteria %p:%p:%p:%p", item, pspec, callback, user_data); } /* see if callback found and it had a disconnect func or notify_func */ if(bag.cb.disconnect) { bag.cb.disconnect(bag.key.item, bag.key.pspec, bag.cb.user_data); } else if(bag.cb.notify_func) { bag.cb.notify_func(bag.cb.user_data); } } /* finds a handler by ID or item/property/callback/user_data and removes it */ static gboolean prop_callback_hash_GHRFunc(gpointer key, gpointer value, gpointer user_data) { PropRemoveBag *bag = (PropRemoveBag *)user_data; PropMatchKey *propkey = (PropMatchKey *)key; GSList *cblist = (GSList *)value, *p, *newroot; PropCallback *cb; p = cblist; while(p) /* loop over callbacks in callback list */ { cb = (PropCallback *)(p->data); /* criteria matches? (by ID or by match) */ if((bag->cb.handler_id && cb->handler_id == bag->cb.handler_id) || (!bag->cb.handler_id && propkey->item == bag->key.item && propkey->pspec == bag->key.pspec && cb->callback == bag->cb.callback && cb->user_data == bag->cb.user_data)) { /* return callback's item, pspec, disconnect func and user_data */ bag->found = TRUE; bag->cb.disconnect = cb->disconnect; bag->cb.notify_func = cb->notify_func; bag->cb.user_data = cb->user_data; bag->key.item = propkey->item; bag->key.pspec = propkey->pspec; g_slice_free(PropCallback, cb); newroot = g_slist_delete_link(cblist, p); if(!newroot) { return (TRUE); /* no more list? Remove hash entry */ } /* if root not the same, return update information (can't be done in GHRFunc) */ if(newroot != cblist) { bag->update_needed = TRUE; bag->update_key = *(PropMatchKey *)key; bag->update_list = newroot; } return (FALSE); } p = g_slist_next(p); } return (FALSE); } libinstpatch-1.1.6/libinstpatch/IpatchIter.c000066400000000000000000000456171400263525300211040ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchIter * @short_description: Iterator instance * @see_also: * @stability: Stable * * A boxed type (structure) used for abstracting manipulation of object lists. */ #include #include "IpatchIter.h" /* IpatchIter methods for GSList type lists */ IpatchIterMethods ipatch_iter_GSList_methods = { ipatch_iter_GSList_get, ipatch_iter_GSList_next, ipatch_iter_GSList_first, ipatch_iter_GSList_last, ipatch_iter_GSList_index, ipatch_iter_GSList_insert, ipatch_iter_GSList_remove, ipatch_iter_GSList_count }; /* IpatchIter methods for GList type lists */ IpatchIterMethods ipatch_iter_GList_methods = { ipatch_iter_GList_get, ipatch_iter_GList_next, ipatch_iter_GList_first, ipatch_iter_GList_last, ipatch_iter_GList_index, ipatch_iter_GList_insert, ipatch_iter_GList_remove, ipatch_iter_GList_count }; /* IpatchIter methods for arrays */ IpatchIterMethods ipatch_iter_array_methods = { ipatch_iter_array_get, ipatch_iter_array_next, ipatch_iter_array_first, ipatch_iter_array_last, ipatch_iter_array_index, ipatch_iter_array_insert, ipatch_iter_array_remove, ipatch_iter_array_count }; /** * ipatch_iter_get_type: * * Gets the GBoxed derived type for #IpatchIter structures. * * Returns: GType of #IpatchIter structures. */ GType ipatch_iter_get_type(void) { static GType type = 0; if(!type) type = g_boxed_type_register_static("IpatchIter", (GBoxedCopyFunc)ipatch_iter_duplicate, (GBoxedFreeFunc)ipatch_iter_free); return (type); } /** * ipatch_iter_alloc: (skip) * * Allocates an item iterator. This function is seldom used since * #IpatchIter structures are usually allocated on the stack. * * Returns: Newly allocated item iterator. Should be freed with * ipatch_iter_free() when finished with it. */ IpatchIter * ipatch_iter_alloc(void) { IpatchIter *iter; iter = g_new0(IpatchIter, 1); return (iter); } /** * ipatch_iter_free: (skip) * @iter: Item iterator * * Frees an item iterator that was allocated with ipatch_iter_alloc(). * Seldom used since #IpatchIter structures are usually allocated on the * stack. */ void ipatch_iter_free(IpatchIter *iter) { g_free(iter); } /** * ipatch_iter_duplicate: (skip) * @iter: Patch iterator to duplicate * * Duplicates a patch iterator. Seldom used since #IpatchIter * structures are usually allocated on the stack and can be copied * directly. * * Returns: Newly allocated patch iter identical to @iter. Free it with * ipatch_iter_free() when finished. */ IpatchIter * ipatch_iter_duplicate(IpatchIter *iter) { IpatchIter *newiter; newiter = ipatch_iter_alloc(); *newiter = *iter; return (newiter); } /** * ipatch_iter_GSList_init: (skip) * @iter: Iterator to initialize * @list: Pointer to root GSList pointer to initialize iterator to * * Initialize an iterator to iterate over a GSList. */ void ipatch_iter_GSList_init(IpatchIter *iter, GSList **list) { g_return_if_fail(iter != NULL); g_return_if_fail(list != NULL); iter->methods = &ipatch_iter_GSList_methods; IPATCH_ITER_GSLIST_SET_LIST(iter, list); IPATCH_ITER_GSLIST_SET_POS(iter, NULL); } /** * ipatch_iter_GSList_get: (skip) * @iter: Item iterator initialized with a GSList * * GSList item iterator method to get the current item. * * Returns: Current item or %NULL if no current item. */ gpointer ipatch_iter_GSList_get(IpatchIter *iter) { GSList *pos; g_return_val_if_fail(iter != NULL, NULL); pos = IPATCH_ITER_GSLIST_GET_POS(iter); return (pos ? (gpointer)(pos->data) : NULL); } /** * ipatch_iter_GSList_next: (skip) * @iter: Item iterator initialized with a GSList * * GSList item iterator method to get the next item and advance the * iterator's position. * * Returns: Next item or %NULL if no more items. */ gpointer ipatch_iter_GSList_next(IpatchIter *iter) { GSList *pos; g_return_val_if_fail(iter != NULL, NULL); pos = IPATCH_ITER_GSLIST_GET_POS(iter); if(pos) { pos = g_slist_next(pos); } IPATCH_ITER_GSLIST_SET_POS(iter, pos); /* set current position */ return (pos ? (gpointer)(pos->data) : NULL); } /** * ipatch_iter_GSList_first: (skip) * @iter: Item iterator initialized with a GSList * * GSList item iterator method to get the first item and set the * iterator's position to it. * * Returns: First item or %NULL if GSList is empty. */ gpointer ipatch_iter_GSList_first(IpatchIter *iter) { GSList **list, *pos; g_return_val_if_fail(iter != NULL, NULL); list = IPATCH_ITER_GSLIST_GET_LIST(iter); /* list pointer */ g_return_val_if_fail(list != NULL, NULL); pos = *list; IPATCH_ITER_GSLIST_SET_POS(iter, pos); /* set position */ return (pos ? (gpointer)(pos->data) : NULL); } /** * ipatch_iter_GSList_last: (skip) * @iter: Item iterator initialized with a GSList * * GSList item iterator method to get the last item and set the * iterator's position to it. * * Returns: Last item or %NULL if GSList is empty. */ gpointer ipatch_iter_GSList_last(IpatchIter *iter) { GSList **list, *pos; g_return_val_if_fail(iter != NULL, NULL); list = IPATCH_ITER_GSLIST_GET_LIST(iter); g_return_val_if_fail(list != NULL, NULL); pos = g_slist_last(*list); IPATCH_ITER_GSLIST_SET_POS(iter, pos); /* set current position */ return (pos ? (gpointer)(pos->data) : NULL); } /** * ipatch_iter_GSList_index: (skip) * @iter: Item iterator initialized with a GSList * @index: Index, from 0, of item to get * * GSList item iterator method to get an item at a given index and set the * iterator's position to it. * * Returns: item at the @index position or %NULL if index is off * the end of the GSList. */ gpointer ipatch_iter_GSList_index(IpatchIter *iter, int index) { GSList **list, *pos; g_return_val_if_fail(iter != NULL, NULL); list = IPATCH_ITER_GSLIST_GET_LIST(iter); g_return_val_if_fail(list != NULL, NULL); pos = g_slist_nth(*list, index); IPATCH_ITER_GSLIST_SET_POS(iter, pos); /* set current position */ return (pos ? (gpointer)(pos->data) : NULL); } /** * ipatch_iter_GSList_insert: (skip) * @iter: Item iterator initialized with a GSList * @item: Pointer to insert * * GSList item iterator method to insert an item pointer. */ void ipatch_iter_GSList_insert(IpatchIter *iter, gpointer item) { GSList **list, *pos; g_return_if_fail(iter != NULL); if((pos = IPATCH_ITER_GSLIST_GET_POS(iter))) /* position set? */ { pos = g_slist_insert(pos, item, 1); /* insert after position */ IPATCH_ITER_GSLIST_SET_POS(iter, g_slist_next(pos)); /* update pos */ } else /* position not set */ { list = IPATCH_ITER_GSLIST_GET_LIST(iter); g_return_if_fail(list != NULL); pos = g_slist_prepend(*list, item); /* prepend */ IPATCH_ITER_GSLIST_SET_POS(iter, pos); /* set current position */ *list = pos; /* set root of list */ } } /** * ipatch_iter_GSList_remove: (skip) * @iter: Item iterator initialized with a GSList * * GSList item iterator method to remove the current item and advance * the current position. */ void ipatch_iter_GSList_remove(IpatchIter *iter) { GSList **list, *pos; g_return_if_fail(iter != NULL); list = IPATCH_ITER_GSLIST_GET_LIST(iter); g_return_if_fail(list != NULL); /* advance current position if set */ pos = IPATCH_ITER_GSLIST_GET_POS(iter); if(pos) { IPATCH_ITER_GSLIST_SET_POS(iter, g_slist_next(pos)); *list = g_slist_delete_link(*list, pos); } } /** * ipatch_iter_GSList_count: (skip) * @iter: Item iterator initialized with a GSList * * GSList item iterator method to get the count of items. * * Returns: Count of items in GSList iterator. */ int ipatch_iter_GSList_count(IpatchIter *iter) { GSList **list; g_return_val_if_fail(iter != NULL, 0); list = IPATCH_ITER_GSLIST_GET_LIST(iter); g_return_val_if_fail(list != NULL, 0); return (g_slist_length(*list)); } /** * ipatch_iter_GList_init: (skip) * @iter: Iterator to initialize * @list: Pointer to root GList pointer to initialize iterator to * * Initialize an iterator to iterate over a GList. */ void ipatch_iter_GList_init(IpatchIter *iter, GList **list) { g_return_if_fail(iter != NULL); iter->methods = &ipatch_iter_GList_methods; IPATCH_ITER_GLIST_SET_LIST(iter, list); IPATCH_ITER_GLIST_SET_POS(iter, NULL); } /** * ipatch_iter_GList_get: (skip) * @iter: Item iterator initialized with a GList * * GList item iterator method to get the current item. * * Returns: Current item or %NULL if no current item. */ gpointer ipatch_iter_GList_get(IpatchIter *iter) { GList *pos; g_return_val_if_fail(iter != NULL, NULL); pos = IPATCH_ITER_GLIST_GET_POS(iter); return (pos ? (gpointer)(pos->data) : NULL); } /** * ipatch_iter_GList_next: (skip) * @iter: Item iterator initialized with a GList * * GList item iterator method to get the next item and advance the * iterator's position. * * Returns: Next item or %NULL if no more items. */ gpointer ipatch_iter_GList_next(IpatchIter *iter) { GList *pos; g_return_val_if_fail(iter != NULL, NULL); pos = IPATCH_ITER_GLIST_GET_POS(iter); if(pos) { pos = g_list_next(pos); } IPATCH_ITER_GLIST_SET_POS(iter, pos); /* set current position */ return (pos ? (gpointer)(pos->data) : NULL); } /** * ipatch_iter_GList_first: (skip) * @iter: Item iterator initialized with a GList * * GList item iterator method to get the first item and set the * iterator's position to it. * * Returns: First item or %NULL if GList is empty. */ gpointer ipatch_iter_GList_first(IpatchIter *iter) { GList **list, *pos; g_return_val_if_fail(iter != NULL, NULL); list = IPATCH_ITER_GLIST_GET_LIST(iter); /* list pointer */ g_return_val_if_fail(list != NULL, NULL); pos = *list; IPATCH_ITER_GLIST_SET_POS(iter, pos); /* set position */ return (pos ? (gpointer)(pos->data) : NULL); } /** * ipatch_iter_GList_last: (skip) * @iter: Item iterator initialized with a GList * * GList item iterator method to get the last item and set the * iterator's position to it. * * Returns: Last item or %NULL if GList is empty. */ gpointer ipatch_iter_GList_last(IpatchIter *iter) { GList **list, *pos; g_return_val_if_fail(iter != NULL, NULL); list = IPATCH_ITER_GLIST_GET_LIST(iter); g_return_val_if_fail(list != NULL, NULL); pos = g_list_last(*list); IPATCH_ITER_GLIST_SET_POS(iter, pos); /* set current position */ return (pos ? (gpointer)(pos->data) : NULL); } /** * ipatch_iter_GList_index: (skip) * @iter: Item iterator initialized with a GList * @index: Index, from 0, of item to get * * GList item iterator method to get an item at a given index and set the * iterator's position to it. * * Returns: item at the @index position or %NULL if index is off * the end of the GList. */ gpointer ipatch_iter_GList_index(IpatchIter *iter, int index) { GList **list, *pos; g_return_val_if_fail(iter != NULL, NULL); list = IPATCH_ITER_GLIST_GET_LIST(iter); g_return_val_if_fail(list != NULL, NULL); pos = g_list_nth(*list, index); IPATCH_ITER_GLIST_SET_POS(iter, pos); /* set current position */ return (pos ? (gpointer)(pos->data) : NULL); } /** * ipatch_iter_GList_insert: (skip) * @iter: Item iterator initialized with a GList * @item: Pointer to insert * * GList item iterator method to insert an item pointer. */ void ipatch_iter_GList_insert(IpatchIter *iter, gpointer item) { GList **list, *pos; g_return_if_fail(iter != NULL); if((pos = IPATCH_ITER_GLIST_GET_POS(iter))) /* position set? */ { pos = g_list_insert(pos, item, 1); /* insert after position */ IPATCH_ITER_GLIST_SET_POS(iter, g_list_next(pos)); /* update pos */ } else /* position not set */ { list = IPATCH_ITER_GLIST_GET_LIST(iter); g_return_if_fail(list != NULL); pos = g_list_prepend(*list, item); /* prepend */ IPATCH_ITER_GLIST_SET_POS(iter, pos); /* set current position */ *list = pos; /* set root of list */ } } /** * ipatch_iter_GList_remove: (skip) * @iter: Item iterator initialized with a GList * * GList item iterator method to remove the current item and advance * the current position. */ void ipatch_iter_GList_remove(IpatchIter *iter) { GList **list, *pos; g_return_if_fail(iter != NULL); list = IPATCH_ITER_GLIST_GET_LIST(iter); g_return_if_fail(list != NULL); /* advance current position if set */ pos = IPATCH_ITER_GLIST_GET_POS(iter); if(pos) { IPATCH_ITER_GLIST_SET_POS(iter, g_list_next(pos)); *list = g_list_delete_link(*list, pos); } } /** * ipatch_iter_GList_count: (skip) * @iter: Item iterator initialized with a GList * * GList item iterator method to get the count of items. * * Returns: Count of items in GList iterator. */ int ipatch_iter_GList_count(IpatchIter *iter) { GList **list; g_return_val_if_fail(iter != NULL, 0); list = IPATCH_ITER_GLIST_GET_LIST(iter); g_return_val_if_fail(list != NULL, 0); return (g_list_length(*list)); } /** * ipatch_iter_array_init: (skip) * @iter: Iterator to initialize * @array: Pointer to an array of pointers * @size: Count of elements in @array. * * Initialize an iterator to iterate over an array (read only). */ void ipatch_iter_array_init(IpatchIter *iter, gpointer *array, guint size) { g_return_if_fail(iter != NULL); g_return_if_fail(array != NULL); iter->methods = &ipatch_iter_array_methods; IPATCH_ITER_ARRAY_SET_ARRAY(iter, array); IPATCH_ITER_ARRAY_SET_SIZE(iter, size); IPATCH_ITER_ARRAY_SET_POS(iter, -1); /* init to no position */ } /** * ipatch_iter_array_get: (skip) * @iter: Item iterator initialized with an array * * Array item iterator method to get the current item. * * Returns: Current item or %NULL if no current item. */ gpointer ipatch_iter_array_get(IpatchIter *iter) { gpointer *array; int pos; g_return_val_if_fail(iter != NULL, NULL); array = IPATCH_ITER_ARRAY_GET_ARRAY(iter); g_return_val_if_fail(array != NULL, NULL); pos = IPATCH_ITER_ARRAY_GET_POS(iter); return ((pos != -1) ? array[pos] : NULL); } /** * ipatch_iter_array_next: (skip) * @iter: Item iterator initialized with an array * * Array item iterator method to get the next item and advance the * iterator's position. * * Returns: Next item or %NULL if no more items. */ gpointer ipatch_iter_array_next(IpatchIter *iter) { gpointer *array; guint pos, size; g_return_val_if_fail(iter != NULL, NULL); array = IPATCH_ITER_ARRAY_GET_ARRAY(iter); g_return_val_if_fail(array != NULL, NULL); pos = (guint)IPATCH_ITER_ARRAY_GET_POS(iter); size = IPATCH_ITER_ARRAY_GET_SIZE(iter); if(pos >= 0 && (pos + 1) < size) { pos++; } else { pos = -1; } IPATCH_ITER_ARRAY_SET_POS(iter, pos); /* update position */ return ((pos != -1) ? array[pos] : NULL); } /** * ipatch_iter_array_first: (skip) * @iter: Item iterator initialized with an array * * Array item iterator method to get the first item and set the * iterator's position to it. * * Returns: First item or %NULL if array is empty. */ gpointer ipatch_iter_array_first(IpatchIter *iter) { gpointer *array; int pos = 0; guint size; g_return_val_if_fail(iter != NULL, NULL); array = IPATCH_ITER_ARRAY_GET_ARRAY(iter); g_return_val_if_fail(array != NULL, NULL); size = IPATCH_ITER_ARRAY_GET_SIZE(iter); if(size == 0) { pos = -1; } IPATCH_ITER_ARRAY_SET_POS(iter, pos); return ((pos != -1) ? array[pos] : NULL); } /** * ipatch_iter_array_last: (skip) * @iter: Item iterator initialized with an array * * Array item iterator method to get the last item and set the * iterator's position to it. * * Returns: Last item or %NULL if array is empty. */ gpointer ipatch_iter_array_last(IpatchIter *iter) { gpointer *array; int pos; guint size; g_return_val_if_fail(iter != NULL, NULL); array = IPATCH_ITER_ARRAY_GET_ARRAY(iter); g_return_val_if_fail(array != NULL, NULL); size = IPATCH_ITER_ARRAY_GET_SIZE(iter); if(size > 0) { pos = size - 1; } else { pos = -1; } IPATCH_ITER_ARRAY_SET_POS(iter, pos); return ((pos != -1) ? array[pos] : NULL); } /** * ipatch_iter_array_index: (skip) * @iter: Item iterator initialized with an array * @index: Index, from 0, of item to get * * Array item iterator method to get an item at a given index and set the * iterator's position to it. * * Returns: item at the @index position or %NULL if index is off * the end of the array. */ gpointer ipatch_iter_array_index(IpatchIter *iter, int index) { gpointer *array; int size; g_return_val_if_fail(iter != NULL, NULL); array = IPATCH_ITER_ARRAY_GET_ARRAY(iter); g_return_val_if_fail(array != NULL, NULL); size = (int)IPATCH_ITER_ARRAY_GET_SIZE(iter); if(index < 0 || index >= size) { index = -1; } IPATCH_ITER_ARRAY_SET_POS(iter, index); return ((index != -1) ? array[index] : NULL); } /** * ipatch_iter_array_insert: (skip) * @iter: Item iterator initialized with a array * @item: Pointer to insert * * array item iterator method to insert an item pointer. */ void ipatch_iter_array_insert(IpatchIter *iter, gpointer item) { g_return_if_reached(); } /** * ipatch_iter_array_remove: (skip) * @iter: Item iterator initialized with a array * * array item iterator method to remove the current item and advance * the current position. */ void ipatch_iter_array_remove(IpatchIter *iter) { g_return_if_reached(); } /** * ipatch_iter_array_count: (skip) * @iter: Item iterator initialized with a array * * array item iterator method to get the count of items. * * Returns: Count of items in array iterator. */ int ipatch_iter_array_count(IpatchIter *iter) { guint size; g_return_val_if_fail(iter != NULL, 0); size = IPATCH_ITER_ARRAY_GET_SIZE(iter); return (size); } libinstpatch-1.1.6/libinstpatch/IpatchIter.h000066400000000000000000000121521400263525300210750ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_ITER_H__ #define __IPATCH_ITER_H__ #include #include typedef struct _IpatchIter IpatchIter; typedef struct _IpatchIterMethods IpatchIterMethods; /* boxed type for IpatchIter */ #define IPATCH_TYPE_ITER (ipatch_iter_get_type ()) /* list iterator structure */ struct _IpatchIter { /*< private >*/ IpatchIterMethods *methods; /* iterator methods */ gpointer data; /* method defined data */ gpointer data2; /* method defined data */ gpointer data3; /* method defined data */ gpointer data4; /* method defined data */ }; /* iterator methods */ struct _IpatchIterMethods { gpointer(*get)(IpatchIter *iter); /* get item method */ gpointer(*next)(IpatchIter *iter); /* next item method */ gpointer(*first)(IpatchIter *iter); /* first item method */ gpointer(*last)(IpatchIter *iter); /* last item method */ gpointer(*index)(IpatchIter *iter, int index); /* index item method */ void (*insert)(IpatchIter *iter, gpointer item); /* insert item method */ void (*remove)(IpatchIter *iter); /* remove current item method */ int (*count)(IpatchIter *iter); /* count items method */ }; GType ipatch_iter_get_type(void); IpatchIter *ipatch_iter_alloc(void); void ipatch_iter_free(IpatchIter *iter); IpatchIter *ipatch_iter_duplicate(IpatchIter *iter); #define ipatch_iter_get(iter) (((iter)->methods->get)(iter)) #define ipatch_iter_next(iter) (((iter)->methods->next)(iter)) #define ipatch_iter_first(iter) (((iter)->methods->first)(iter)) #define ipatch_iter_last(iter) (((iter)->methods->last)(iter)) #define ipatch_iter_index(iter, pos) (((iter)->methods->index)(iter, pos)) #define ipatch_iter_insert(iter, item) (((iter)->methods->insert)(iter, item)) #define ipatch_iter_remove(iter) (((iter)->methods->remove)(iter)) #define ipatch_iter_count(iter) (((iter)->methods->count)(iter)) #define IPATCH_ITER_GSLIST_GET_LIST(iter) ((GSList **)(iter->data)) #define IPATCH_ITER_GSLIST_GET_POS(iter) ((GSList *)(iter->data2)) #define IPATCH_ITER_GSLIST_SET_LIST(iter, list) (iter->data = list) #define IPATCH_ITER_GSLIST_SET_POS(iter, pos) (iter->data2 = pos) void ipatch_iter_GSList_init(IpatchIter *iter, GSList **list); gpointer ipatch_iter_GSList_get(IpatchIter *iter); gpointer ipatch_iter_GSList_next(IpatchIter *iter); gpointer ipatch_iter_GSList_first(IpatchIter *iter); gpointer ipatch_iter_GSList_last(IpatchIter *iter); gpointer ipatch_iter_GSList_index(IpatchIter *iter, int index); void ipatch_iter_GSList_insert(IpatchIter *iter, gpointer item); void ipatch_iter_GSList_remove(IpatchIter *iter); int ipatch_iter_GSList_count(IpatchIter *iter); #define IPATCH_ITER_GLIST_GET_LIST(iter) ((GList **)(iter->data)) #define IPATCH_ITER_GLIST_GET_POS(iter) ((GList *)(iter->data2)) #define IPATCH_ITER_GLIST_SET_LIST(iter, list) (iter->data = list) #define IPATCH_ITER_GLIST_SET_POS(iter, pos) (iter->data2 = pos) void ipatch_iter_GList_init(IpatchIter *iter, GList **list); gpointer ipatch_iter_GList_get(IpatchIter *iter); gpointer ipatch_iter_GList_next(IpatchIter *iter); gpointer ipatch_iter_GList_first(IpatchIter *iter); gpointer ipatch_iter_GList_last(IpatchIter *iter); gpointer ipatch_iter_GList_index(IpatchIter *iter, int index); void ipatch_iter_GList_insert(IpatchIter *iter, gpointer item); void ipatch_iter_GList_remove(IpatchIter *iter); int ipatch_iter_GList_count(IpatchIter *iter); #define IPATCH_ITER_ARRAY_GET_ARRAY(iter) ((gpointer *)(iter->data)) #define IPATCH_ITER_ARRAY_GET_SIZE(iter) (GPOINTER_TO_UINT (iter->data2)) #define IPATCH_ITER_ARRAY_GET_POS(iter) (GPOINTER_TO_INT (iter->data3)) #define IPATCH_ITER_ARRAY_SET_ARRAY(iter, array) (iter->data = array) #define IPATCH_ITER_ARRAY_SET_SIZE(iter, size) \ (iter->data2 = GUINT_TO_POINTER (size)) #define IPATCH_ITER_ARRAY_SET_POS(iter, pos) \ (iter->data3 = GINT_TO_POINTER (pos)) void ipatch_iter_array_init(IpatchIter *iter, gpointer *array, guint size); gpointer ipatch_iter_array_get(IpatchIter *iter); gpointer ipatch_iter_array_next(IpatchIter *iter); gpointer ipatch_iter_array_first(IpatchIter *iter); gpointer ipatch_iter_array_last(IpatchIter *iter); gpointer ipatch_iter_array_index(IpatchIter *iter, int index); void ipatch_iter_array_insert(IpatchIter *iter, gpointer item); void ipatch_iter_array_remove(IpatchIter *iter); int ipatch_iter_array_count(IpatchIter *iter); #endif libinstpatch-1.1.6/libinstpatch/IpatchList.c000066400000000000000000000163501400263525300211040ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchList * @short_description: An object containing a list of object pointers * @see_also: * @stability: Stable * * An object which defines a list of object pointers. A #GObject reference * is held to all objects until the #IpatchList itself is released. */ #include #include #include "IpatchList.h" static void ipatch_list_class_init(IpatchListClass *klass); static void ipatch_list_finalize(GObject *gobject); static GObjectClass *parent_class = NULL; GType ipatch_list_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchListClass), NULL, NULL, (GClassInitFunc) ipatch_list_class_init, NULL, NULL, sizeof(IpatchList), 0, (GInstanceInitFunc) NULL, }; item_type = g_type_register_static(G_TYPE_OBJECT, "IpatchList", &item_info, 0); } return (item_type); } static void ipatch_list_class_init(IpatchListClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->finalize = ipatch_list_finalize; } static void ipatch_list_finalize(GObject *gobject) { IpatchList *list = IPATCH_LIST(gobject); GList *p; p = list->items; while(p) /* unref all objects in list and destroy the list */ { g_object_unref(p->data); p = g_list_delete_link(p, p); } list->items = NULL; if(parent_class->finalize) { parent_class->finalize(gobject); } } /** * ipatch_list_new: * * Create a new object list object. #IpatchList objects are often used to * duplicate multi-thread sensitive object lists, so they can be iterated over * at one's own leasure. * * Returns: New object list container object. */ IpatchList * ipatch_list_new(void) { return (IPATCH_LIST(g_object_new(IPATCH_TYPE_LIST, NULL))); } /** * ipatch_list_duplicate: * @list: Object list to duplicate * * Duplicate an object list. * * Returns: (transfer full): New duplicate object list with a ref count of one which the * caller owns. */ IpatchList * ipatch_list_duplicate(IpatchList *list) { IpatchList *newlist; GList *p; g_return_val_if_fail(IPATCH_IS_LIST(list), NULL); newlist = ipatch_list_new(); /* ++ ref new list */ p = list->items; while(p) { if(p->data) { g_object_ref(p->data); /* ++ ref object for new list */ } newlist->items = g_list_prepend(newlist->items, p->data); p = g_list_next(p); } newlist->items = g_list_reverse(newlist->items); return (newlist); /* !! caller owns the new list reference */ } /** * ipatch_list_get_items: * @list: List object * * Get the items list in a #IpatchList object. Mainly for the benefit of * GObject Introspection, since it is common practice to just access the * items field directly. * * Returns: (element-type GObject) (transfer none): The list of items in * the @list object, which is owned by the @list and should not be * modified or freed directly. * * Since: 1.1.0 */ GList * ipatch_list_get_items(IpatchList *list) { g_return_val_if_fail(IPATCH_IS_LIST(list), NULL); return list->items; } /** * ipatch_list_set_items: * @list: List object * @items: (element-type GObject) (transfer full): List of #GObject pointers to assign, * @list takes over ownership, each object should be referenced already for the list * * Set the items list in a #IpatchList object. Mainly for the benefit of * GObject Introspection, since it is common practice to just access the * items field directly. Replaces existing items * (list if any). * * Since: 1.1.0 */ void ipatch_list_set_items(IpatchList *list, GList *items) { GList *p; g_return_if_fail(IPATCH_IS_LIST(list)); for(p = list->items; p; p = g_list_delete_link(p, p)) { g_object_unref(p->data); } list->items = items; // !! list takes over items } /** * ipatch_list_append: * @list: List object * @object: Object to append to the list * * Append an object to an #IpatchList. * * Since: 1.1.0 */ void ipatch_list_append(IpatchList *list, GObject *object) { g_return_if_fail(IPATCH_IS_LIST(list)); g_return_if_fail(G_IS_OBJECT(object)); g_object_ref(object); // ++ ref for list list->items = g_list_append(list->items, object); } /** * ipatch_list_prepend: * @list: List object * @object: Object to prepend to the list * * Prepend an object to an #IpatchList. * * Since: 1.1.0 */ void ipatch_list_prepend(IpatchList *list, GObject *object) { g_return_if_fail(IPATCH_IS_LIST(list)); g_return_if_fail(G_IS_OBJECT(object)); g_object_ref(object); // ++ ref for list list->items = g_list_prepend(list->items, object); } /** * ipatch_list_insert: * @list: List object * @object: Object to insert into the list * @pos: Position to insert into (0 for start of list, -1 to append) * * Append an object to an #IpatchList. * * Since: 1.1.0 */ void ipatch_list_insert(IpatchList *list, GObject *object, int pos) { g_return_if_fail(IPATCH_IS_LIST(list)); g_return_if_fail(G_IS_OBJECT(object)); g_object_ref(object); // ++ ref for list list->items = g_list_insert(list->items, object, pos); } /** * ipatch_list_remove: * @list: List object * @object: Object to remove from the list * * Remove an object from an #IpatchList. * * Returns: %TRUE if found and removed, %FALSE otherwise * * Since: 1.1.0 */ gboolean ipatch_list_remove(IpatchList *list, GObject *object) { GList *p; g_return_val_if_fail(IPATCH_IS_LIST(list), FALSE); g_return_val_if_fail(G_IS_OBJECT(object), FALSE); p = g_list_find(list->items, object); if(!p) { return (FALSE); } g_object_unref(p->data); // -- unref object list->items = g_list_delete_link(list->items, p); return (TRUE); } /** * ipatch_list_init_iter: (skip) * @list: List object * @iter: Iterator to initialize * * Initializes a user supplied iterator (usually stack allocated) to iterate * over the object @list. Further operations on @iter will use the @list. */ void ipatch_list_init_iter(IpatchList *list, IpatchIter *iter) { g_return_if_fail(IPATCH_IS_LIST(list)); g_return_if_fail(iter != NULL); ipatch_iter_GList_init(iter, &list->items); } libinstpatch-1.1.6/libinstpatch/IpatchList.h000066400000000000000000000044461400263525300211140ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_LIST_H__ #define __IPATCH_LIST_H__ #include #include typedef struct _IpatchList IpatchList; typedef struct _IpatchListClass IpatchListClass; #include #define IPATCH_TYPE_LIST (ipatch_list_get_type ()) #define IPATCH_LIST(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_LIST, IpatchList)) #define IPATCH_LIST_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_LIST, IpatchListClass)) #define IPATCH_IS_LIST(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_LIST)) #define IPATCH_IS_LIST_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_LIST)) /* an object containing a duplicated list of referenced objects */ struct _IpatchList { GObject parent_instance; /*< public >*/ GList *items; /* list of GObject items */ }; /* class for iterator list object */ struct _IpatchListClass { GObjectClass parent_class; }; GType ipatch_list_get_type(void); IpatchList *ipatch_list_new(void); IpatchList *ipatch_list_duplicate(IpatchList *list); GList *ipatch_list_get_items(IpatchList *list); void ipatch_list_set_items(IpatchList *list, GList *items); void ipatch_list_append(IpatchList *list, GObject *object); void ipatch_list_prepend(IpatchList *list, GObject *object); void ipatch_list_insert(IpatchList *list, GObject *object, int pos); gboolean ipatch_list_remove(IpatchList *list, GObject *object); void ipatch_list_init_iter(IpatchList *list, IpatchIter *iter); #endif libinstpatch-1.1.6/libinstpatch/IpatchParamProp.c000066400000000000000000000374211400263525300220740ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchParamProp * @short_description: GParamSpec extended properties * @see_also: * @stability: Stable * * Extensions to standard GParamSpec include flags (for * compact single bit data extensions) and GValue based extensions. * An example of usage is the IPATCH_PARAM_UNIQUE flag which indicates * a parameter that should be unique amongst sibling items and the * "string-max-length" integer GValue which specifies a max * length of a GParamSpecString parameter. */ #include #include #include #include #include #include "IpatchParamProp.h" #include "i18n.h" static void param_list_properties_GHFunc(gpointer key, gpointer value, gpointer user_data); static void param_set_property(GParamSpec *spec, GParamSpec *prop_spec, const GValue *value); static void param_value_destroy_notify(gpointer user_data); static gboolean param_get_property(GParamSpec *spec, GParamSpec *prop_spec, GValue *value); G_LOCK_DEFINE_STATIC(param_prop_hash); /* GParamSpec GValue property hash */ static GHashTable *param_prop_hash = NULL; /* Initialization/deinitialization of GParamSpec extended properties system */ /** * _ipatch_param_init: (skip) * * Initialize parameter/unit system */ void _ipatch_param_init(void) { param_prop_hash = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)g_param_spec_unref); /* install string length property */ ipatch_param_install_property (g_param_spec_uint("string-max-length", _("Max Length"), _("Max string length (0=no limit)"), 0, G_MAXUINT, 0, G_PARAM_READWRITE)); /* install floating point digits property */ ipatch_param_install_property (g_param_spec_uint("float-digits", _("Float Digits"), _("Significant decimal digits"), 0, 64, 2, G_PARAM_READWRITE)); /* install unique group ID property */ ipatch_param_install_property (g_param_spec_uint("unique-group-id", _("Unique group ID"), _("For grouping multiple unique properties"), 0, G_MAXUINT, 0, G_PARAM_READWRITE)); /* install unit type property */ ipatch_param_install_property (g_param_spec_uint("unit-type", _("Units"), _("Type of units used"), 0, G_MAXUINT, 0, G_PARAM_READWRITE)); } /* Freeing GParamSpec extended properties system */ void _ipatch_param_deinit(void) { g_hash_table_destroy(param_prop_hash); } /* -- API of GParamSpec extended properties system ---------------------*/ /** * ipatch_param_install_property: * @prop_spec: Specification for the new #GParamSpec property. Ownership * of the GParamSpec is taken over by this function. The name field of * the GParamSpec should be a static string and is used as the property's * ID. * * Install a new #GParamSpec property which can be used to extend existing * #GParamSpec types or define common parameters shared by all types. An * example property is the "string-max-length" property which defines a * max length for use with #GParamSpecString properties. */ void ipatch_param_install_property(GParamSpec *prop_spec) { GQuark quark; g_return_if_fail(G_IS_PARAM_SPEC(prop_spec)); g_return_if_fail(prop_spec->name != NULL); /* take ownership of the parameter spec */ g_param_spec_ref(prop_spec); g_param_spec_sink(prop_spec); quark = g_quark_from_static_string(prop_spec->name); G_LOCK(param_prop_hash); g_hash_table_insert(param_prop_hash, GUINT_TO_POINTER(quark), prop_spec); G_UNLOCK(param_prop_hash); } /** * ipatch_param_find_property: * @name: Name of GParamSpec property * * Lookup a GParamSpec property by name. * * Returns: (transfer none): The matching GParamSpec or %NULL if not found. * The GParamSpec is internal and should NOT be modified or freed. */ GParamSpec * ipatch_param_find_property(const char *name) { GParamSpec *spec; GQuark quark; g_return_val_if_fail(name != NULL, NULL); quark = g_quark_try_string(name); if(!quark) { return (NULL); } G_LOCK(param_prop_hash); spec = g_hash_table_lookup(param_prop_hash, GUINT_TO_POINTER(quark)); G_UNLOCK(param_prop_hash); return (spec); } /** * ipatch_param_list_properties: * @n_properties: (out): Length of returned array * * Get a list of all registered GParamSpec properties. * * Returns: (array length=n_properties) (transfer container): An array of * GParamSpecs which should be freed when finished. */ GParamSpec ** ipatch_param_list_properties(guint *n_properties) { GParamSpec **specs, **specp; g_return_val_if_fail(n_properties != NULL, NULL); G_LOCK(param_prop_hash); specs = g_new(GParamSpec *, g_hash_table_size(param_prop_hash)); specp = specs; g_hash_table_foreach(param_prop_hash, param_list_properties_GHFunc, &specp); G_UNLOCK(param_prop_hash); return (specs); } static void param_list_properties_GHFunc(gpointer key, gpointer value, gpointer user_data) { GParamSpec ***specs = user_data; **specs = (GParamSpec *)value; *specs = *specs + 1; } /** * ipatch_param_set: * @spec: Parameter spec to set extended properties of * @first_property_name: Name of first property to set * @...: Value of first property to set and optionally followed by more * property name/value pairs, terminated with %NULL name. * * Set extended parameter properties. Parameter properties are used to * extend existing #GParamSpec types. "string-max-length" is an example of * an extended property, which is used for #GParamSpecString parameters to * define the max allowed length. * * Returns: (transfer none): The @spec pointer for convenience, makes it easy * to create/install a parameter spec and set its properties at the same time. */ GParamSpec * ipatch_param_set(GParamSpec *spec, const char *first_property_name, ...) { va_list args; va_start(args, first_property_name); ipatch_param_set_valist(spec, first_property_name, args); va_end(args); return (spec); } /** * ipatch_param_set_valist: * @spec: Parameter spec to set extended properties of * @first_property_name: Name of first property to set * @args: Value of first property to set and optionally followed by more * property name/value pairs, terminated with %NULL name. * * Like ipatch_param_set() but uses a va_list. */ void ipatch_param_set_valist(GParamSpec *spec, const char *first_property_name, va_list args) { const char *name; GValue value = { 0, }; gchar *error = NULL; GParamSpec *prop_spec; g_return_if_fail(G_IS_PARAM_SPEC(spec)); g_return_if_fail(first_property_name != NULL); name = first_property_name; while(name) { prop_spec = ipatch_param_find_property(name); if(!prop_spec) { g_warning("%s: no parameter property named `%s'", G_STRLOC, name); break; } if(!(prop_spec->flags & G_PARAM_WRITABLE)) { g_warning("%s: parameter property `%s' is not writable", G_STRLOC, prop_spec->name); break; } g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(prop_spec)); G_VALUE_COLLECT(&value, args, 0, &error); if(error) { g_warning("%s: %s", G_STRLOC, error); g_free(error); /* we purposely leak the value here, it might not be * in a sane state if an error condition occured */ break; } param_set_property(spec, prop_spec, &value); g_value_unset(&value); name = va_arg(args, char *); } } /** * ipatch_param_set_property: * @spec: Parameter spec to set an extended property of * @property_name: Name of property to set * @value: Value to set the the property to. The value's * type must be the same as the parameter property's type. * * Set a single extended parameter property of a #GParamSpec. */ void ipatch_param_set_property(GParamSpec *spec, const char *property_name, const GValue *value) { GParamSpec *prop_spec; g_return_if_fail(G_IS_PARAM_SPEC(spec)); g_return_if_fail(property_name != NULL); g_return_if_fail(G_IS_VALUE(value)); prop_spec = ipatch_param_find_property(property_name); if(!prop_spec) { g_warning("%s: no parameter property named `%s'", G_STRLOC, property_name); return; } if(!(prop_spec->flags & G_PARAM_WRITABLE)) { g_warning("%s: parameter property `%s' is not writable", G_STRLOC, property_name); return; } if(G_VALUE_TYPE(value) != G_PARAM_SPEC_VALUE_TYPE(prop_spec)) { g_warning("%s: value type should be '%s' but is '%s'", G_STRLOC, g_type_name(G_PARAM_SPEC_VALUE_TYPE(prop_spec)), G_VALUE_TYPE_NAME(value)); return; } param_set_property(spec, prop_spec, value); } /* does the actual setting of a GParamSpec GValue property */ static void param_set_property(GParamSpec *spec, GParamSpec *prop_spec, const GValue *value) { GQuark quark; GValue *newval; quark = g_quark_try_string(prop_spec->name); g_return_if_fail(quark != 0); /* duplicate the GValue */ newval = g_new0(GValue, 1); g_value_init(newval, G_VALUE_TYPE(value)); g_value_copy(value, newval); /* set the GParamSpec property with a destroy notify to free the value if the property gets set again */ g_param_spec_set_qdata_full(spec, quark, newval, (GDestroyNotify)param_value_destroy_notify); } /* destroy notify for GParamSpec extended GValue properties */ static void param_value_destroy_notify(gpointer user_data) { GValue *value = (GValue *)user_data; g_value_unset(value); g_free(value); } /** * ipatch_param_get: * @spec: Parameter spec to get extended properties from * @first_property_name: Name of first property to get * @...: Pointer to store first property value and optionally followed * by more property name/value pairs, terminated with %NULL name. * * Get extended parameter properties. Parameter properties are used to * extend existing #GParamSpec types. "string-max-length" is an example of * an extended property, which is used for #GParamSpecString parameters to * define the max allowed length. */ void ipatch_param_get(GParamSpec *spec, const char *first_property_name, ...) { va_list args; va_start(args, first_property_name); ipatch_param_get_valist(spec, first_property_name, args); va_end(args); } /** * ipatch_param_get_valist: * @spec: Parameter spec to get extended properties from * @first_property_name: Name of first property to get * @args: Pointer to store first property value and optionally followed * by more property name/value pairs, terminated with %NULL name. * * Like ipatch_param_get() but uses a va_list. */ void ipatch_param_get_valist(GParamSpec *spec, const char *first_property_name, va_list args) { const char *name; g_return_if_fail(G_IS_PARAM_SPEC(spec)); g_return_if_fail(first_property_name != NULL); name = first_property_name; while(name) { GValue value = { 0, }; GParamSpec *prop_spec; char *error; prop_spec = ipatch_param_find_property(name); if(!prop_spec) { g_warning("%s: no parameter property named `%s'", G_STRLOC, name); break; } if(!(prop_spec->flags & G_PARAM_READABLE)) { g_warning("%s: parameter property `%s' is not readable", G_STRLOC, prop_spec->name); break; } g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(prop_spec)); param_get_property(spec, prop_spec, &value); G_VALUE_LCOPY(&value, args, 0, &error); if(error) { g_warning("%s: %s", G_STRLOC, error); g_free(error); g_value_unset(&value); break; } g_value_unset(&value); name = va_arg(args, char *); } } /** * ipatch_param_get_property: * @spec: Parameter spec to get an extended property from * @property_name: Name of property to get * @value: (out): An initialized value to store the property value in. The value's * type must be a type that the property can be transformed to. * * Get a single extended parameter property from a #GParamSpec. * * Returns: %TRUE if the parameter property is set, %FALSE otherwise (in which * case @value will contain the default value for this property). */ gboolean ipatch_param_get_property(GParamSpec *spec, const char *property_name, GValue *value) { GParamSpec *prop_spec; gboolean retval = FALSE; g_return_val_if_fail(G_IS_PARAM_SPEC(spec), FALSE); g_return_val_if_fail(property_name != NULL, FALSE); g_return_val_if_fail(G_IS_VALUE(value), FALSE); prop_spec = ipatch_param_find_property(property_name); if(!prop_spec) g_warning("%s: no parameter property named `%s'", G_STRLOC, property_name); else if(!(prop_spec->flags & G_PARAM_READABLE)) g_warning("%s: parameter property `%s' is not readable", G_STRLOC, prop_spec->name); else { GValue *prop_value, tmp_value = { 0, }; /* auto-conversion of the callers value type */ if(G_VALUE_TYPE(value) == G_PARAM_SPEC_VALUE_TYPE(prop_spec)) { g_value_reset(value); prop_value = value; } else if(!g_value_type_transformable(G_PARAM_SPEC_VALUE_TYPE(prop_spec), G_VALUE_TYPE(value))) { g_warning("can't retrieve parameter property `%s' of type" " `%s' as value of type `%s'", prop_spec->name, g_type_name(G_PARAM_SPEC_VALUE_TYPE(prop_spec)), G_VALUE_TYPE_NAME(value)); return (FALSE); } else { g_value_init(&tmp_value, G_PARAM_SPEC_VALUE_TYPE(prop_spec)); prop_value = &tmp_value; } retval = param_get_property(spec, prop_spec, prop_value); if(prop_value != value) { g_value_transform(prop_value, value); g_value_unset(&tmp_value); } } return (retval); } /* does the actual getting of a GParamSpec GValue property */ static gboolean param_get_property(GParamSpec *spec, GParamSpec *prop_spec, GValue *value) { GQuark quark; GValue *getval; quark = g_quark_try_string(prop_spec->name); g_return_val_if_fail(quark != 0, FALSE); getval = g_param_spec_get_qdata(spec, quark); if(!getval) { g_param_value_set_default(prop_spec, value); } else { g_value_copy(getval, value); } return (getval != NULL); } libinstpatch-1.1.6/libinstpatch/IpatchParamProp.h000066400000000000000000000054641400263525300221030ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_PARAM_PROP_H__ #define __IPATCH_PARAM_PROP_H__ #include #include /* libInstPatch GParamSpec->flags */ enum { /* Parameter should be unique amongst siblings */ IPATCH_PARAM_UNIQUE = (1 << G_PARAM_USER_SHIFT), /* Hint that a property should be hidden in user interfaces */ IPATCH_PARAM_HIDE = (1 << (G_PARAM_USER_SHIFT + 1)), /* Indicates that property affects audio synthesis */ IPATCH_PARAM_SYNTH = (1 << (G_PARAM_USER_SHIFT + 2)), /* Indicates that property can be a real time synthesis parameter */ IPATCH_PARAM_SYNTH_REALTIME = (1 << (G_PARAM_USER_SHIFT + 3)), /* Used for properties which don't modify the saveable state of an object. * The object's base object save dirty flag wont get set. */ IPATCH_PARAM_NO_SAVE_CHANGE = (1 << (G_PARAM_USER_SHIFT + 4)), /* Indicates that property should not be saved as object state (XML for example) */ IPATCH_PARAM_NO_SAVE = (1 << (G_PARAM_USER_SHIFT + 5)) }; /* next shift value usable by libInstPatch user in GParamSpec->flags */ #define IPATCH_PARAM_USER_SHIFT (G_PARAM_USER_SHIFT + 12) void ipatch_param_install_property(GParamSpec *prop_spec); GParamSpec *ipatch_param_find_property(const char *name); GParamSpec **ipatch_param_list_properties(guint *n_properties); GParamSpec *ipatch_param_set(GParamSpec *spec, const char *first_property_name, ...); void ipatch_param_set_valist(GParamSpec *spec, const char *first_property_name, va_list args); void ipatch_param_set_property(GParamSpec *spec, const char *property_name, const GValue *value); void ipatch_param_get(GParamSpec *spec, const char *first_property_name, ...); void ipatch_param_get_valist(GParamSpec *spec, const char *first_property_name, va_list args); gboolean ipatch_param_get_property(GParamSpec *spec, const char *property_name, GValue *value); #endif libinstpatch-1.1.6/libinstpatch/IpatchPaste.c000066400000000000000000001557251400263525300212570ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchPaste * @short_description: Object paste instance * @see_also: * @stability: Stable * * This object provides a system and instance for doing cut/paste operations * on instrument items. */ #include #include #include #include "misc.h" #include "IpatchPaste.h" #include "IpatchBase.h" #include "IpatchConverter.h" #include "IpatchContainer.h" #include "IpatchTypeProp.h" #include "IpatchVirtualContainer.h" #include "builtin_enums.h" #include "marshals.h" #include "ipatch_priv.h" #include "i18n.h" #include "util.h" typedef struct { IpatchPasteTestFunc test_func; IpatchPasteExecFunc exec_func; GDestroyNotify notify_func; gpointer user_data; int id; int flags; } PasteHandler; /* info for an item add operation */ typedef struct { IpatchItem *additem; /* item to add */ IpatchContainer *parent; /* parent to add additem to */ IpatchItem *conflict; /* conflict item (if any) */ IpatchPasteChoice choice; /* choice of how to handle conflict */ } AddItemBag; /* info for an item link operation */ typedef struct { IpatchItem *from; /* object to link from (set property) */ IpatchItem *to; /* object to link to */ } LinkItemBag; static void _ipatch_paste_free_handler(PasteHandler *data); static gint handler_priority_GCompareFunc(gconstpointer a, gconstpointer b); static void ipatch_paste_class_init(IpatchPasteClass *klass); static void ipatch_paste_init(IpatchPaste *paste); static void ipatch_paste_finalize(GObject *gobject); static guint resolve_hash_func(gconstpointer key); static gboolean resolve_equal_func(gconstpointer a, gconstpointer b); static void resolve_key_destroy_func(gpointer data); static guint check_hash_func(gconstpointer key); static gboolean check_equal_func(gconstpointer a, gconstpointer b); static void check_item_conflicts_GHFunc(gpointer key, gpointer value, gpointer user_data); static IpatchItem *paste_copy_link_func_deep(IpatchItem *item, IpatchItem *link, gpointer user_data); static GStaticRecMutex paste_handlers_m = G_STATIC_REC_MUTEX_INIT; static GSList *paste_handlers = NULL; /* list of PasteHandler structs */ static int ipatch_paste_handler_id = 0; // handler ID counter static gpointer parent_class = NULL; /* ----- Initialization/deinitialization of list ----------------------------*/ /* Initialize handlers list */ void _ipatch_paste_init(void) { paste_handlers = NULL; ipatch_paste_handler_id = 0; } /* Free list */ void _ipatch_paste_deinit(void) { g_slist_free_full(paste_handlers, (GDestroyNotify)_ipatch_paste_free_handler); } /* free paste handler data */ static void _ipatch_paste_free_handler(PasteHandler *data) { g_slice_free(PasteHandler,data); } /*------ IpatchPaste object functions --------------------------------------*/ /** * ipatch_register_paste_handler: (skip) * @test_func: Callback function to test if a paste operation is handled * @exec_func: Paste execution function * @flags: (type IpatchPastePriority): Currently just a value * from #IpatchPastePriority or 0 for default priority. * * Registers a handler function to paste objects for * which @test_func returns %TRUE. */ void ipatch_register_paste_handler(IpatchPasteTestFunc test_func, IpatchPasteExecFunc exec_func, int flags) { ipatch_register_paste_handler_full(test_func, exec_func, NULL, NULL, flags); } /** * ipatch_register_paste_handler_full: (rename-to ipatch_register_paste_handler) * @test_func: (scope notified): Callback function to test if a paste operation is handled * @exec_func: (scope notified): Paste execution function * @notify_func: (nullable) (scope async) (closure user_data): Called when paste * handler is unregistered * @user_data: (nullable): Data to pass to @notify_func or %NULL * @flags: (type IpatchPastePriority): Currently just a value * from #IpatchPastePriority or 0 for default priority. * * Registers a handler function to paste objects for * which @test_func returns %TRUE. Like ipatch_register_paste_handler() but is friendly * to GObject Introspection. * * Returns: Handler ID, which can be used to unregister it or -1 if invalid parameters * * Since: 1.1.0 */ int ipatch_register_paste_handler_full(IpatchPasteTestFunc test_func, IpatchPasteExecFunc exec_func, GDestroyNotify notify_func, gpointer user_data, int flags) { PasteHandler *handler; int id; g_return_val_if_fail(test_func != NULL, -1); g_return_val_if_fail(exec_func != NULL, -1); if(flags == 0) { flags = IPATCH_PASTE_PRIORITY_DEFAULT; } handler = g_slice_new(PasteHandler); // ++ alloc handler structure handler->test_func = test_func; handler->exec_func = exec_func; handler->notify_func = notify_func; handler->user_data = user_data; handler->flags = flags; g_static_rec_mutex_lock(&paste_handlers_m); id = ++ipatch_paste_handler_id; handler->id = id; paste_handlers = g_slist_insert_sorted(paste_handlers, handler, handler_priority_GCompareFunc); g_static_rec_mutex_unlock(&paste_handlers_m); return (id); } static gint handler_priority_GCompareFunc(gconstpointer a, gconstpointer b) { PasteHandler *ahandler = (PasteHandler *)a, *bhandler = (PasteHandler *)b; /* priority sorts from highest to lowest so subtract a from b */ return ((bhandler->flags & IPATCH_PASTE_FLAGS_PRIORITY_MASK) - (ahandler->flags & IPATCH_PASTE_FLAGS_PRIORITY_MASK)); } /** * ipatch_unregister_paste_handler: * @id: ID of handler which was returned from ipatch_register_paste_handler_full(). * * Unregister a paste handler. * * Returns: %TRUE if found and unregistered, %FALSE otherwise * * Since: 1.1.0 */ gboolean ipatch_unregister_paste_handler(int id) { PasteHandler *handler; GDestroyNotify notify_func = NULL; gpointer user_data; GSList *p; g_static_rec_mutex_lock(&paste_handlers_m); for(p = paste_handlers; p; p = p->next) { handler = (PasteHandler *)(p->data); if(handler->id == id) { paste_handlers = g_slist_delete_link(paste_handlers, p); notify_func = handler->notify_func; user_data = handler->user_data; g_slice_free(PasteHandler, handler); // -- free handler structure } } g_static_rec_mutex_unlock(&paste_handlers_m); if(notify_func) { notify_func(user_data); } return (p != NULL); } /** * ipatch_simple_paste: * @dest: Destination item to paste to * @src: Source item * @err: Location to store error info or %NULL * * Simple paste of a single @src item to @dest item. Any conflicts are * ignored which means that conflicts will remain and should be resolved. * * Returns: %TRUE on success, %FALSE otherwise in which case @err should be set. */ gboolean ipatch_simple_paste(IpatchItem *dest, IpatchItem *src, GError **err) { IpatchPaste *paste; g_return_val_if_fail(IPATCH_IS_ITEM(dest), FALSE); g_return_val_if_fail(IPATCH_IS_ITEM(src), FALSE); g_return_val_if_fail(!err || !*err, FALSE); paste = ipatch_paste_new(); /* ++ ref new object */ /* setup object paste */ if(!ipatch_paste_objects(paste, dest, src, err)) { /* object paste failed */ g_object_unref(paste); /* -- unref paste object */ return (FALSE); } /* finish paste operation */ if(!ipatch_paste_finish(paste, err)) { /* finish paste failed */ g_object_unref(paste); /* -- unref paste object */ return (FALSE); } g_object_unref(paste); return (TRUE); } /** * ipatch_is_paste_possible: * @dest: Destination item * @src: Source item * * Check if the given items can be pasted from @src to @dest. * * Returns: %TRUE if paste is possible, %FALSE otherwise */ gboolean ipatch_is_paste_possible(IpatchItem *dest, IpatchItem *src) { PasteHandler *handler; GSList *p; g_return_val_if_fail(IPATCH_IS_ITEM(dest), FALSE); g_return_val_if_fail(IPATCH_IS_ITEM(src), FALSE); g_static_rec_mutex_lock(&paste_handlers_m); for(p = paste_handlers; p; p = p->next) { handler = (PasteHandler *)(p->data); if(handler->test_func(dest, src)) { break; } } g_static_rec_mutex_unlock(&paste_handlers_m); return (p != NULL); } GType ipatch_paste_get_type(void) { static GType obj_type = 0; if(!obj_type) { static const GTypeInfo obj_info = { sizeof(IpatchPasteClass), NULL, NULL, (GClassInitFunc)ipatch_paste_class_init, NULL, NULL, sizeof(IpatchPaste), 0, (GInstanceInitFunc)ipatch_paste_init, }; obj_type = g_type_register_static(G_TYPE_OBJECT, "IpatchPaste", &obj_info, 0); /* register the default handler */ ipatch_register_paste_handler(ipatch_paste_default_test_func, ipatch_paste_default_exec_func, 0); } return (obj_type); } static void ipatch_paste_class_init(IpatchPasteClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->finalize = ipatch_paste_finalize; } static void ipatch_paste_init(IpatchPaste *paste) { paste->add_hash = g_hash_table_new(NULL, NULL); } /* function called when a patch is being destroyed */ static void ipatch_paste_finalize(GObject *gobject) { IpatchPaste *paste = IPATCH_PASTE(gobject); AddItemBag *addbag; LinkItemBag *linkbag; GSList *p; g_hash_table_destroy(paste->add_hash); for(p = paste->add_list; p; p = p->next) { addbag = (AddItemBag *)(p->data); g_object_unref(addbag->additem); g_object_unref(addbag->parent); if(addbag->conflict) { g_object_unref(addbag->conflict); } } for(p = paste->link_list; p; p = p->next) { linkbag = (LinkItemBag *)(p->data); g_object_unref(linkbag->from); g_object_unref(linkbag->to); } if(G_OBJECT_CLASS(parent_class)->finalize) { G_OBJECT_CLASS(parent_class)->finalize(gobject); } } /* * ipatch_paste_new: * * Create a new paste object for patch object paste operations. * * Returns: New paste object with refcount of 1 which caller owns. */ IpatchPaste * ipatch_paste_new(void) { return (IPATCH_PASTE(g_object_new(IPATCH_TYPE_PASTE, NULL))); } /** * ipatch_paste_objects: * @paste: Paste object * @dest: Destination item of paste * @src: Source item of paste * @err: Location to store error info or %NULL * * Setup a paste operation. Multiple item pastes can occur for the same * @paste instance. Existing duplicated items are used if present (example: * if multiple instruments are pasted between different IpatchBase objects * and they link to the same sample, they will both use the same sample in * the final paste operation). * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set) */ gboolean ipatch_paste_objects(IpatchPaste *paste, IpatchItem *dest, IpatchItem *src, GError **err) { PasteHandler *handler = NULL; GSList *p; g_return_val_if_fail(IPATCH_IS_PASTE(paste), FALSE); g_return_val_if_fail(IPATCH_IS_ITEM(dest), FALSE); g_return_val_if_fail(IPATCH_IS_ITEM(src), FALSE); g_return_val_if_fail(!err || !*err, FALSE); g_static_rec_mutex_lock(&paste_handlers_m); for(p = paste_handlers; p; p = p->next) { handler = (PasteHandler *)(p->data); if(handler->test_func(dest, src)) { break; } } g_static_rec_mutex_unlock(&paste_handlers_m); if(!p) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNHANDLED_CONVERSION, _("Pasting object of type %s to %s is unsupported"), G_OBJECT_TYPE_NAME(src), G_OBJECT_TYPE_NAME(dest)); return (FALSE); } return (handler->exec_func(paste, dest, src, err)); } /* hash key used by ipatch_paste_resolve */ typedef struct { /* NOTE: item and parent are ref'd by paste->add_list, no need here */ IpatchItem *item; /* item for these property values */ IpatchContainer *parent; /* parent of item (or proposed parent) */ GValueArray *valarray; /* array of all of item's unique prop. values */ GParamSpec **pspecs; /* parameter specs for these properties (NULL term.) */ guint8 index; /* index to first value/pspec of this unique group */ guint8 count; /* # of values/pspecs in this unique prop group */ guint8 free_valarray; /* boolean, TRUE if hash should free valarray */ } ResolveHashKey; /* bag used for passing multiple vars to check_item_conflicts_GHFunc */ typedef struct { IpatchPaste *paste; GHashTable *confl_hash; IpatchPasteResolveFunc resolve_func; gboolean cancel; } CheckBag; /** * ipatch_paste_resolve: * @paste: Paste object * @resolve_func: (scope call) (closure user_data): Resolve callback function * which is invoked for each conflict. * @user_data: User defined data to pass to @resolve_func. * * This function is used to make choices as to how conflicts are resolved. * Conflicting objects are those with identical unique property values. For * each conflicting object that would result from a paste, the @resolve_func is * called allowing a choice to be made as to how it is handled. The default * choice is to ignore the duplicate, resulting in conflicting objects. * This function can be executed multiple times, the choices are only executed * once ipatch_paste_finish() is called. * * Returns: %TRUE on success, %FALSE if operation was canceled (@resolve_func * returned #IPATCH_PASTE_CHOICE_CANCEL). */ gboolean ipatch_paste_resolve(IpatchPaste *paste, IpatchPasteResolveFunc resolve_func, gpointer user_data) { GHashTable *confl_hash; /* hash of add item unique property values */ GHashTable *check_hash; /* hash of parent children to check */ GParamSpec **pspecs, **ps; AddItemBag *addbag, *confl_addbag; ResolveHashKey *key, skey; CheckBag checkbag; GValueArray *valarray; gboolean free_valarray; guint32 groups; GSList *p; int i, lastbit, count, index, choice; g_return_val_if_fail(IPATCH_IS_PASTE(paste), FALSE); g_return_val_if_fail(resolve_func != NULL, FALSE); /* create conflict hash, we try and increase performance by hashing items and their unique property values (rather than having to compare every item to every other possible conflicting item) */ confl_hash = g_hash_table_new_full(resolve_hash_func, resolve_equal_func, resolve_key_destroy_func, NULL); /* hash of Parent:ChildType to check for conflicts. Uses existing ResolveHashKey structures. */ check_hash = g_hash_table_new(check_hash_func, check_equal_func); /* add paste items to hash and detect conflicts with other paste items */ for(p = paste->add_list; p; p = p->next) { addbag = (AddItemBag *)(p->data); /* get item's unique property values (if any) */ valarray = ipatch_item_get_unique_props(addbag->additem); if(!valarray) { continue; /* no unique props? skip */ } free_valarray = TRUE; /* get corresponding property param specs and group bits */ pspecs = ipatch_item_type_get_unique_specs(G_OBJECT_TYPE(addbag->additem), &groups); if(!pspecs) /* should never happen! */ { g_value_array_free(valarray); continue; } lastbit = (groups & 1); count = 0; index = 0; /* loop over unique property groups */ for(i = 0, ps = pspecs; ; i++, ps++, groups >>= 1) { if(!*ps || lastbit != (groups & 1)) /* last val or end of group? */ { /* initialize the static hash key */ skey.valarray = valarray; skey.item = addbag->additem; skey.parent = addbag->parent; skey.pspecs = pspecs; skey.index = index; skey.count = count; /* check for conflict within paste items */ confl_addbag = g_hash_table_lookup(confl_hash, &skey); if(!confl_addbag) /* no conflict? */ { /* add to conflict detect hash */ key = g_new(ResolveHashKey, 1); *key = skey; /* copy static key to allocated key */ key->free_valarray = free_valarray; /* free once only */ g_hash_table_insert(confl_hash, key, addbag); free_valarray = FALSE; /* hash will free it */ /* Parent:ItemType not yet added to check_hash? */ if(!g_hash_table_lookup(check_hash, key)) { g_hash_table_insert(check_hash, key, addbag); } if(!*ps) { break; /* NULL pspec terminator? */ } } else /* we have a conflict, tell caller about it */ { choice = resolve_func(paste, confl_addbag->additem, addbag->additem); /* cancel requested? */ if(choice == IPATCH_PASTE_CHOICE_CANCEL) { g_hash_table_destroy(confl_hash); g_hash_table_destroy(check_hash); return (FALSE); } addbag->conflict = g_object_ref(confl_addbag->additem); addbag->choice = choice; } index = i; /* next group index */ count = 1; /* reset group count */ lastbit = (groups & 1); } else { count++; /* not end of group, increment current val count */ } } /* if valarray was not used in hash, then free it here */ if(free_valarray) { g_value_array_free(valarray); } } /* bag for passing multilple variables to hash foreach function */ checkbag.paste = paste; checkbag.confl_hash = confl_hash; checkbag.resolve_func = resolve_func; checkbag.cancel = FALSE; g_hash_table_foreach(check_hash, check_item_conflicts_GHFunc, &checkbag); g_hash_table_destroy(confl_hash); g_hash_table_destroy(check_hash); if(checkbag.cancel) { return (FALSE); } return (TRUE); } /* hash function used for detecting conflicting items quickly (in theory) */ static guint resolve_hash_func(gconstpointer key) { ResolveHashKey *rkey = (ResolveHashKey *)key; GValueArray *valarray; GValue *value; guint hashval; int i, end; /* hash the parent, item type and group value index */ hashval = GPOINTER_TO_UINT(rkey->parent); hashval += G_OBJECT_TYPE(rkey->item); i = rkey->index; hashval += i; valarray = rkey->valarray; end = i + rkey->count; /* value end index */ for(; i < end; i++) /* hash the property values for this group */ { value = g_value_array_get_nth(valarray, i); hashval += ipatch_util_value_hash(value); } return (hashval); } /* hash key compare func for ResolveHashKeys */ static gboolean resolve_equal_func(gconstpointer a, gconstpointer b) { ResolveHashKey *akey = (ResolveHashKey *)a, *bkey = (ResolveHashKey *)b; GValue *aval, *bval; int i, end; /* value index, parent or item type not the same? */ if(akey->index != bkey->index || akey->parent != bkey->parent || G_OBJECT_TYPE(akey->item) != G_OBJECT_TYPE(bkey->item)) { return (FALSE); } i = akey->index; end = i + akey->count; for(; i < end; i++) /* see if the property values are the same */ { aval = g_value_array_get_nth(akey->valarray, i); bval = g_value_array_get_nth(bkey->valarray, i); if(g_param_values_cmp(akey->pspecs[i], aval, bval) != 0) { return (FALSE); } } return (TRUE); /* keys match (conflict) */ } static void resolve_key_destroy_func(gpointer data) { ResolveHashKey *rkey = (ResolveHashKey *)data; if(rkey->free_valarray) { g_value_array_free(rkey->valarray); } g_free(rkey); } /* hash function used for hashing parent:ItemTypes to check later for duplicates. They are hashed so that we only have to check the given children once. */ static guint check_hash_func(gconstpointer key) { ResolveHashKey *rkey = (ResolveHashKey *)key; return (GPOINTER_TO_UINT(rkey->parent) + G_OBJECT_TYPE(rkey->item)); } /* hash key compare func for Parent:ItemType check hash */ static gboolean check_equal_func(gconstpointer a, gconstpointer b) { ResolveHashKey *akey = (ResolveHashKey *)a, *bkey = (ResolveHashKey *)b; return (akey->parent == bkey->parent && G_OBJECT_TYPE(akey->item) == G_OBJECT_TYPE(bkey->item)); } /* check for conflicts in existing items using Parent:ItemType hash */ static void check_item_conflicts_GHFunc(gpointer key, gpointer value, gpointer user_data) { CheckBag *checkbag = (CheckBag *)user_data; ResolveHashKey *rkey = (ResolveHashKey *)key; ResolveHashKey skey; /* static key */ IpatchPasteResolveFunc resolve_func = (IpatchPasteResolveFunc)user_data; AddItemBag *confl_addbag; GParamSpec **pspecs, **ps; GValueArray *valarray; guint32 groups; IpatchIter iter; IpatchItem *item; IpatchList *list; int i, lastbit, count, index, choice; if(checkbag->cancel) { return; /* operation cancelled? */ } /* get children of specific type for parent */ list = ipatch_container_get_children(rkey->parent, G_OBJECT_TYPE(rkey->item)); ipatch_list_init_iter(list, &iter); /* get property param specs and group bits (all items are of same type) */ pspecs = ipatch_item_type_get_unique_specs(G_OBJECT_TYPE(rkey->item), &groups); /* loop over container children */ for(item = ipatch_item_first(&iter); item; item = ipatch_item_next(&iter)) { /* get item's unique property values */ valarray = ipatch_item_get_unique_props(item); if(!valarray) { continue; /* no unique props? skip (shouldn't happen) */ } lastbit = (groups & 1); count = 0; index = 0; /* loop over unique property groups */ for(i = 0, ps = pspecs; ; i++, ps++, groups >>= 1) { if(!*ps || lastbit != (groups & 1)) /* last val or end of group? */ { /* initialize the static hash key */ skey.valarray = valarray; skey.item = item; skey.parent = rkey->parent; skey.pspecs = pspecs; skey.index = index; skey.count = count; /* check for conflict within paste items */ confl_addbag = g_hash_table_lookup(checkbag->confl_hash, &skey); if(confl_addbag) /* conflict? */ { choice = resolve_func(checkbag->paste, item, confl_addbag->additem); /* cancel requested? */ if(choice == IPATCH_PASTE_CHOICE_CANCEL) { checkbag->cancel = TRUE; g_value_array_free(valarray); g_object_unref(list); /* -- unref list */ return; } confl_addbag->conflict = g_object_ref(item); confl_addbag->choice = choice; } index = i; /* next group index */ count = 1; /* reset group count */ lastbit = (groups & 1); } else { count++; /* not end of group, increment current val count */ } g_value_array_free(valarray); } /* for each unique parameter group */ } /* for container items */ g_object_unref(list); /* -- unref list */ } /** * ipatch_paste_finish: * @paste: Paste object * @err: Location to store error info or %NULL * * Complete the paste operation(s) (add/link objects). Conflicts are handled * for the choices made with ipatch_paste_resolve() (defaults to ignore which * will result in conflicts). * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_paste_finish(IpatchPaste *paste, GError **err) { AddItemBag *addbag; LinkItemBag *linkbag; GSList *p; g_return_val_if_fail(IPATCH_IS_PASTE(paste), FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* add items in add list */ for(p = paste->add_list; p; p = p->next) { addbag = (AddItemBag *)(p->data); if(addbag->choice == IPATCH_PASTE_CHOICE_IGNORE) { ipatch_container_add(addbag->parent, addbag->additem); } /* FIXME - Need to implement replace operation */ } /* link items in link list */ for(p = paste->link_list; p; p = p->next) { linkbag = (LinkItemBag *)(p->data); g_object_set(linkbag->from, "link-item", linkbag->to, NULL); } return (TRUE); } /** * ipatch_paste_get_add_list: * @paste: Paste object * * Get list of objects to add with paste operation. This can be called * after ipatch_paste_objects() or after ipatch_paste_finish(). In the first * case the objects have not yet been added, in the second case the paste * operation has been completed. The list of objects returned are only those * which are not conflicting or a choice of #IPATCH_PASTE_CHOICE_IGNORE or * #IPATCH_PASTE_CHOICE_REPLACE was selected. * * Returns: (transfer full): List of objects being added with paste operation or %NULL if none. * Returned list has a refcount of 1 which the caller owns, unref when done. */ IpatchList * ipatch_paste_get_add_list(IpatchPaste *paste) { IpatchList *retlist; GList *newlist = NULL; AddItemBag *bag; GSList *p; g_return_val_if_fail(IPATCH_IS_PASTE(paste), NULL); for(p = paste->add_list; p; p = p->next) { bag = (AddItemBag *)(p->data); if(bag->choice == IPATCH_PASTE_CHOICE_IGNORE || bag->choice == IPATCH_PASTE_CHOICE_REPLACE) { newlist = g_list_prepend(newlist, g_object_ref(bag->additem)); } } if(newlist) { retlist = ipatch_list_new(); /* ++ ref list */ retlist->items = g_list_reverse(newlist); /* reverse the list, since we prepended */ return (retlist); /* !! caller takes over reference */ } return (NULL); } /** * ipatch_paste_object_add: * @paste: Paste object * @additem: New item to add. * @parent: Container to parent @additem to. * @orig: (nullable): Original item associated with @additem (if duplicated for example). * If supplied then an association between the @orig object and the @additem * will be made, and any references to @orig of subsequent deep duplications * will use the new @additem instead. * * Used by #IpatchPasteExecFunc handlers. Adds an object addition operation * to a paste instance. */ void ipatch_paste_object_add(IpatchPaste *paste, IpatchItem *additem, IpatchContainer *parent, IpatchItem *orig) { AddItemBag *addbag; g_return_if_fail(IPATCH_IS_PASTE(paste)); g_return_if_fail(IPATCH_IS_ITEM(additem)); g_return_if_fail(IPATCH_IS_CONTAINER(parent)); g_return_if_fail(!orig || IPATCH_IS_ITEM(orig)); /* create a bag to hold the item add info and add to add_list */ addbag = g_new(AddItemBag, 1); addbag->additem = g_object_ref(additem); addbag->parent = g_object_ref(parent); addbag->conflict = NULL; addbag->choice = IPATCH_PASTE_CHOICE_IGNORE; if(paste->add_list_last) { paste->add_list_last = g_slist_append(paste->add_list_last, addbag); paste->add_list_last = paste->add_list_last->next; } else /* Empty list */ { paste->add_list = g_slist_append(paste->add_list, addbag); paste->add_list_last = paste->add_list; } /* set up an association to the original item */ if(orig) { g_hash_table_insert(paste->add_hash, orig, addbag); } } /** * ipatch_paste_object_add_duplicate: * @paste: Paste object * @item: Item to duplicate and add * @parent: Container to parent duplicated item to. * * Used by #IpatchPasteExecFunc handlers. Duplicates an item and adds an * addition operation to a paste instance. Useful for duplicating an object * within the same IpatchBase parent. For this reason the duplicated item is * automatically forced to be unique and no association is added for @item to * the new duplicate. * * Returns: (transfer none): The new duplicate of @item (no reference added for caller). */ IpatchItem * ipatch_paste_object_add_duplicate(IpatchPaste *paste, IpatchItem *item, IpatchContainer *parent) { IpatchItem *dup; g_return_val_if_fail(IPATCH_IS_PASTE(paste), NULL); g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL); g_return_val_if_fail(IPATCH_IS_CONTAINER(parent), NULL); dup = ipatch_item_duplicate(item); /* ++ ref new dup */ /* make unique if requested */ ipatch_container_make_unique(IPATCH_CONTAINER(parent), dup); /* add the object add operation of the duplicated item */ ipatch_paste_object_add(paste, dup, IPATCH_CONTAINER(parent), NULL); g_object_unref(dup); /* -- unref dup object */ return (dup); } /* bag used in ipatch_paste_object_add_duplicate_deep() */ typedef struct { IpatchPaste *paste; IpatchContainer *dest_base; } DupDeepBag; /** * ipatch_paste_object_add_duplicate_deep: * @paste: Paste object * @item: Item to deep duplicate and add. * @parent: Container to parent @item to. * * Used by #IpatchPasteExecFunc handlers. Deep duplicates @item and registers * it as an add to @parent in the @paste operation, also registers all new * duplicated dependencies of @item. Any existing matching duplicate items in * the @paste instance are used rather than duplicating them again. * * Returns: (transfer none): The new duplicate of @item (no reference added for caller). */ IpatchItem * ipatch_paste_object_add_duplicate_deep(IpatchPaste *paste, IpatchItem *item, IpatchContainer *parent) { DupDeepBag bag; IpatchItem *dup; g_return_val_if_fail(IPATCH_IS_PASTE(paste), NULL); g_return_val_if_fail(IPATCH_IS_ITEM(item), NULL); g_return_val_if_fail(IPATCH_IS_CONTAINER(parent), NULL); bag.paste = paste; bag.dest_base = IPATCH_CONTAINER (ipatch_item_get_base(IPATCH_ITEM(parent))); /* ++ ref base */ /* deep duplicate the item (custom link function to use existing dups) */ dup = ipatch_item_duplicate_link_func(item, paste_copy_link_func_deep, &bag); /* add the duplicate object addition operation to paste instance */ ipatch_paste_object_add(paste, dup, parent, item); g_object_unref(dup); /* !! paste instance owns ref */ g_object_unref(bag.dest_base); /* -- unref base */ return (dup); } /* IpatchItemCopyLinkFunc for deep duplicating an object and dependencies but using existing dups in paste instance, if any */ static IpatchItem * paste_copy_link_func_deep(IpatchItem *item, IpatchItem *link, gpointer user_data) { DupDeepBag *dupbag = (DupDeepBag *)user_data; AddItemBag *bag; IpatchItem *dup; if(!link) { return (NULL); } /* look up link item in paste add hash */ bag = g_hash_table_lookup(dupbag->paste->add_hash, link); /* FIXME - HACK until SoundFont stereo handling is improved. * Reciprocal stereo linking cluster #&*!s things. */ if(IPATCH_IS_SF2_SAMPLE(item)) { if(!bag) { return (NULL); } /* Re-link the other sample */ ipatch_sf2_sample_set_linked(IPATCH_SF2_SAMPLE(bag->additem), IPATCH_SF2_SAMPLE(item)); return (bag->additem); } if(!bag) /* link not in hash? - Duplicate link and add it to paste. */ { dup = g_object_new(G_OBJECT_TYPE(link), NULL); /* ++ ref new item */ g_return_val_if_fail(dup != NULL, NULL); ipatch_paste_object_add(dupbag->paste, dup, dupbag->dest_base, link); /* recursively copy the link object to the duplicate (finish duping) */ ipatch_item_copy_link_func(dup, link, paste_copy_link_func_deep, user_data); g_object_unref(dup); /* !! paste instance holds a ref */ } else { dup = bag->additem; } return (dup); } /** * ipatch_paste_object_add_convert: * @paste: Paste object * @conv_type: IpatchConverter derived type to use for conversion. * @item: Item to convert and add. * @parent: Container to parent converted item to. * @item_list: (out) (optional): Location to store pointer to the list of added items or %NULL * to ignore. Caller owns a reference to the list. * @err: Location to store error info or %NULL to ignore. * * Used by #IpatchPasteExecFunc handlers. Converts @item using an * #IpatchConverter of type @conv_type and registers * it as an add to @parent in the @paste operation, also registers all new * dependencies of @item. Any existing matching converted item dependencies in * the @paste instance are used rather than duplicating them again. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_paste_object_add_convert(IpatchPaste *paste, GType conv_type, IpatchItem *item, IpatchContainer *parent, IpatchList **item_list, GError **err) { IpatchConverter *converter; const IpatchConverterInfo *convinfo; IpatchList *list; GObject *dest; GList *p; g_return_val_if_fail(IPATCH_IS_PASTE(paste), FALSE); g_return_val_if_fail(g_type_is_a(conv_type, IPATCH_TYPE_CONVERTER), FALSE); g_return_val_if_fail(IPATCH_IS_ITEM(item), FALSE); g_return_val_if_fail(IPATCH_IS_CONTAINER(parent), FALSE); g_return_val_if_fail(!err || !*err, FALSE); convinfo = ipatch_lookup_converter_info(conv_type, 0, 0); g_return_val_if_fail(convinfo != NULL, FALSE); converter = IPATCH_CONVERTER(g_object_new(conv_type, NULL)); /* ++ ref */ g_return_val_if_fail(converter != NULL, FALSE); ipatch_converter_add_input(converter, G_OBJECT(item)); /* check if converter needs its destination item supplied */ if(convinfo->dest_count == IPATCH_CONVERTER_COUNT_ONE_OR_MORE || convinfo->dest_count == 1) { dest = g_object_new(convinfo->dest_type, NULL); /* ++ ref */ if(log_if_fail(dest != NULL)) { g_object_unref(converter); /* -- unref converter */ return (FALSE); } ipatch_converter_add_output(converter, dest); g_object_unref(dest); /* -- unref */ } else if(log_if_fail(convinfo->dest_count == 0)) { g_object_unref(converter); /* -- unref converter */ return (FALSE); } if(!ipatch_converter_convert(converter, err)) { g_object_unref(converter); /* -- unref converter */ return (FALSE); } list = ipatch_converter_get_outputs(converter); /* ++ ref list */ g_object_unref(converter); /* -- unref converter */ /* add objects to paste operation */ for(p = list->items; p; p = p->next) { ipatch_paste_object_add(paste, IPATCH_ITEM(p->data), parent, item); } if(item_list) { *item_list = list; /* !! caller takes over reference */ } else { g_object_unref(list); /* -- unref list */ } return (TRUE); } /** * ipatch_paste_object_link: * @paste: Paste object * @from: Item to link from * @to: Item to link to * * Used by #IpatchPasteExecFunc handlers. Registers a link operation. */ void ipatch_paste_object_link(IpatchPaste *paste, IpatchItem *from, IpatchItem *to) { LinkItemBag *linkbag; g_return_if_fail(IPATCH_IS_PASTE(paste)); g_return_if_fail(IPATCH_IS_ITEM(from)); g_return_if_fail(IPATCH_IS_ITEM(to)); /* create a bag to hold the item link info and add to link_list */ linkbag = g_new(LinkItemBag, 1); linkbag->from = g_object_ref(from); linkbag->to = g_object_ref(to); paste->link_list = g_slist_prepend(paste->link_list, linkbag); } /** * ipatch_paste_default_test_func: * @dest: Destination item of paste operation * @src: Source item of paste operation * * Default #IpatchPasteTestFunc. Useful for alternative paste implementations * which would like to chain to the default function (to override only specific * object types for example). * * Returns: %TRUE if paste supported by this handler, %FALSE otherwise */ gboolean ipatch_paste_default_test_func(IpatchItem *dest, IpatchItem *src) { GType src_type, link_type, type; const GType *child_types = NULL, *ptype; GParamSpec *spec; g_return_val_if_fail(IPATCH_IS_ITEM(dest), FALSE); g_return_val_if_fail(IPATCH_IS_ITEM(src), FALSE); src_type = G_OBJECT_TYPE(src); /* destination is a container? */ if(IPATCH_IS_CONTAINER(dest)) { /* get child types for destination container */ child_types = ipatch_container_get_child_types(IPATCH_CONTAINER(dest)); if(!child_types) { return (FALSE); /* no child types??!! */ } /* check if src type in child types */ for(ptype = child_types; *ptype; ptype++) if(g_type_is_a(src_type, *ptype)) { return (TRUE); /* if child type found, paste supported */ } /* src is a link type of any of container's children types? */ for(ptype = child_types; *ptype; ptype++) { ipatch_type_get(*ptype, "link-type", &link_type, NULL); if(g_type_is_a(src_type, link_type)) { return (TRUE); /* link type found, paste supported */ } } } else if(IPATCH_IS_VIRTUAL_CONTAINER(dest)) /* dest is a virtual container? */ { IpatchItem *child_obj; /* get the child type of the virtual container */ ipatch_type_get(G_OBJECT_TYPE(dest), "virtual-child-type", &type, NULL); /* does source object conform to the virtual container child type? */ if(type && g_type_is_a(G_OBJECT_TYPE(src), type)) { return (TRUE); } /* or can it be pasted to the child type recursively? */ child_obj = g_object_new(type, NULL); /* ++ ref child object */ if(child_obj) { if(ipatch_is_paste_possible(child_obj, src)) { g_object_unref(child_obj); /* -- unref child_obj */ return (TRUE); /* can be pasted recursively into the child type */ } g_object_unref(child_obj); /* -- unref child_obj */ } } else /* destination is not a container - src is link type of dest? */ { /* dest has link item property (FIXME - Future proof?) */ spec = g_object_class_find_property(G_OBJECT_GET_CLASS(dest), "link-item"); /* link item property and src is of the required type? */ if(spec && g_type_is_a(src_type, spec->value_type)) { return (TRUE); } } /* ## see if paste could occur if source object is converted ## */ /* destination is a container? */ if(IPATCH_IS_CONTAINER(dest)) { /* child_types already retrieved above */ /* check if src type can be converted to any child types */ for(ptype = child_types; *ptype; ptype++) { if(ipatch_lookup_converter_info(0, G_OBJECT_TYPE(src), *ptype)) { return (TRUE); } } /* can src be converted to a container's child link type? */ for(ptype = child_types; *ptype; ptype++) { ipatch_type_get(*ptype, "link-type", &link_type, NULL); if(ipatch_lookup_converter_info(0, G_OBJECT_TYPE(src), link_type)) { return (TRUE); } } } else if(IPATCH_IS_VIRTUAL_CONTAINER(dest)) /* dest is a virtual container? */ { /* get the child type of the virtual container */ ipatch_type_get(G_OBJECT_TYPE(dest), "virtual-child-type", &type, NULL); if(type) { /* can object be converted to container child type? */ if(ipatch_lookup_converter_info(0, G_OBJECT_TYPE(src), type)) { return (TRUE); } } } else /* dest is not a container - can convert src to link type of dest? */ { /* dest has link item property (FIXME - Future proof?) */ spec = g_object_class_find_property(G_OBJECT_GET_CLASS(dest), "link-item"); if(!spec) { return (FALSE); } /* can src be converted to link type of dest? */ if(ipatch_lookup_converter_info(0, G_OBJECT_TYPE(src), spec->value_type)) { return (TRUE); } } return (FALSE); } /** * ipatch_paste_default_exec_func: * @paste: Paste object * @src: Source object of paste * @dest: Destination object of paste * @err: Location to store error info or %NULL * * Default #IpatchPasteExecFunc. Useful for alternative paste implementations * which would like to chain to the default function (to override only specific * object types for example). * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_paste_default_exec_func(IpatchPaste *paste, IpatchItem *dest, IpatchItem *src, GError **err) { IpatchItem *src_base, *dest_base; IpatchItem *link, *dup; GParamSpec *spec; GType src_type, link_type, type; const GType *child_types = NULL, *ptype; const IpatchConverterInfo *convinfo, *matchinfo; IpatchVirtualContainerConformFunc conform_func; IpatchList *list; g_return_val_if_fail(IPATCH_IS_PASTE(paste), FALSE); g_return_val_if_fail(IPATCH_IS_ITEM(src), FALSE); g_return_val_if_fail(IPATCH_IS_ITEM(dest), FALSE); g_return_val_if_fail(!err || !*err, FALSE); src_base = ipatch_item_get_base(src); // ++ ref dest_base = ipatch_item_get_base(dest); // ++ ref src_type = G_OBJECT_TYPE(src); /* destination is a container? */ if(IPATCH_IS_CONTAINER(dest)) { /* get child types for destination container */ child_types = ipatch_container_get_child_types(IPATCH_CONTAINER(dest)); if(!child_types) { goto not_handled; } /* check if src type in child types */ for(ptype = child_types; *ptype; ptype++) if(g_type_is_a(src_type, *ptype)) { break; } if(*ptype) /* matching child type found? */ { /* paste is local (within the same IpatchBase?) */ if(src_base == dest_base) { ipatch_paste_object_add_duplicate(paste, src, IPATCH_CONTAINER(dest)); } else /* deep duplicate the object and add to the paste instance */ ipatch_paste_object_add_duplicate_deep(paste, src, IPATCH_CONTAINER(dest)); goto ret_ok; } /* src is a link type of any of container's children types? */ for(ptype = child_types; *ptype; ptype++) { ipatch_type_get(*ptype, "link-type", &link_type, NULL); if(g_type_is_a(src_type, link_type)) { break; } } /* matching link type found? */ if(*ptype) { GObject *newchild; newchild = g_object_new(*ptype, NULL); /* ++ ref new child object */ if(!newchild) { g_warning("Failed to create linked child of type %s -> %s", g_type_name(*ptype), g_type_name(link_type)); goto not_handled; } /* add the object add operation of the new child */ ipatch_paste_object_add(paste, IPATCH_ITEM(newchild), IPATCH_CONTAINER(dest), NULL); /* link the new child item to the source object */ g_object_set(newchild, "link-item", src, NULL); g_object_unref(newchild); /* -- unref creator's ref */ goto ret_ok; /* paste was handled */ } } else if(IPATCH_IS_VIRTUAL_CONTAINER(dest)) /* dest is a virtual container? */ { IpatchItem *newchild; GValue val = { 0 }; /* get the child type of the virtual container */ ipatch_type_get(G_OBJECT_TYPE(dest), "virtual-child-type", &type, "virtual-child-conform-func", &conform_func, NULL); if(!type) { goto not_handled; } /* does source object conform to the virtual container type? */ if(g_type_is_a(G_OBJECT_TYPE(src), type)) { /* if src is foreign, deep duplicate it, otherwise local duplicate */ if(src_base != dest_base) dup = ipatch_paste_object_add_duplicate_deep(paste, src, IPATCH_CONTAINER(dest_base)); else dup = ipatch_paste_object_add_duplicate(paste, src, IPATCH_CONTAINER(dest_base)); if(conform_func) { conform_func(G_OBJECT(dup)); } goto ret_ok; /* paste was handled */ } /* can it be pasted into the child type recursively? */ newchild = g_object_new(type, NULL); /* ++ ref new child object */ if(!newchild) { g_warning("Failed to create child of type %s", g_type_name(type)); goto not_handled; } if(conform_func) { conform_func(G_OBJECT(newchild)); } if(ipatch_is_paste_possible(newchild, src)) { if(!ipatch_simple_paste(newchild, src, err)) { g_object_unref(newchild); /* -- unref creator's ref */ goto ret_err; /* paste not handled */ } /* Inherit title of the new item from the pasted one */ g_value_init(&val, G_TYPE_STRING); g_object_get_property(G_OBJECT(src), "title", &val); g_object_set_property(G_OBJECT(newchild), "name", &val); g_value_unset(&val); ipatch_container_make_unique(IPATCH_CONTAINER(dest_base), newchild); /* add the object add operation of the new child */ ipatch_paste_object_add(paste, IPATCH_ITEM(newchild), IPATCH_CONTAINER(dest_base), NULL); g_object_unref(newchild); /* -- unref creator's ref */ goto ret_ok; /* paste was handled */ } g_object_unref(newchild); /* -- unref creator's ref */ } else /* destination is not a container - src is link type of dest? */ { /* dest has link item property (FIXME - Future proof?) */ spec = g_object_class_find_property(G_OBJECT_GET_CLASS(dest), "link-item"); /* if no link item property or src isn't of the required type - error */ if(!spec || !g_type_is_a(src_type, spec->value_type)) { goto not_handled; } /* if src is foreign, duplicate it */ if(src_base != dest_base) link = ipatch_paste_object_add_duplicate_deep (paste, src, IPATCH_CONTAINER(dest_base)); else { link = src; } /* add the link operation */ ipatch_paste_object_link(paste, dest, link); goto ret_ok; /* we done */ } /* ## see if paste could occur if source object is converted ## */ /* destination is a container? */ if(IPATCH_IS_CONTAINER(dest)) { /* child_types already retrieved above */ /* check if src type can be converted to any child types, pick the highest rated converter */ matchinfo = NULL; for(ptype = child_types; *ptype; ptype++) { convinfo = ipatch_lookup_converter_info(0, G_OBJECT_TYPE(src), *ptype); if(convinfo && (!matchinfo || convinfo->priority > matchinfo->priority)) { matchinfo = convinfo; } } if(matchinfo) /* found a converter match? */ { if(ipatch_paste_object_add_convert(paste, matchinfo->conv_type, src, IPATCH_CONTAINER(dest), NULL, err)) { goto ret_ok; } else { goto ret_err; } } /* can src be converted to a container's child link type? */ for(ptype = child_types; *ptype; ptype++) { ipatch_type_get(*ptype, "link-type", &link_type, NULL); convinfo = ipatch_lookup_converter_info(0, G_OBJECT_TYPE(src), link_type); if(convinfo && (!matchinfo || convinfo->priority > matchinfo->priority)) { matchinfo = convinfo; } } /* matching converter found? */ if(matchinfo) { GObject *newchild; /* convert the source object to the matching link type */ if(!ipatch_paste_object_add_convert(paste, matchinfo->conv_type, src, IPATCH_CONTAINER(dest_base), &list, err)) /* ++ ref list */ { goto ret_err; } newchild = g_object_new(*ptype, NULL); /* ++ ref new child object */ if(!newchild) { g_warning("Failed to create linked child of type %s -> %s", g_type_name(*ptype), g_type_name(link_type)); g_object_unref(list); /* -- unref list */ goto not_handled; } /* add the object add operation of the new child */ ipatch_paste_object_add(paste, IPATCH_ITEM(newchild), IPATCH_CONTAINER(dest), NULL); /* link the new child item to the converted source object */ g_object_set(newchild, "link-item", list->items->data, NULL); g_object_unref(newchild); /* -- unref creator's ref */ g_object_unref(list); /* -- unref list */ goto ret_ok; /* paste was handled */ } } else if(IPATCH_IS_VIRTUAL_CONTAINER(dest)) /* dest is a virtual container? */ { /* get the child type of the virtual container */ ipatch_type_get(G_OBJECT_TYPE(dest), "virtual-child-type", &type, "virtual-child-conform-func", &conform_func, NULL); if(!type) { goto not_handled; } /* can object be converted to container child type? */ convinfo = ipatch_lookup_converter_info(0, G_OBJECT_TYPE(src), type); if(!convinfo) { goto not_handled; } /* add the conversion operation to the paste */ if(!ipatch_paste_object_add_convert(paste, convinfo->conv_type, src, IPATCH_CONTAINER(dest_base), &list, err)) /* ++ ref list */ { goto ret_err; } if(conform_func) { conform_func(G_OBJECT(list->items->data)); } g_object_unref(list); /* -- unref list */ goto ret_ok; /* paste was handled */ } else /* dest is not a container - can convert src to link type of dest? */ { /* dest has link item property (FIXME - Future proof?) */ spec = g_object_class_find_property(G_OBJECT_GET_CLASS(dest), "link-item"); if(!spec) { goto not_handled; } /* can src be converted to link type of dest? */ convinfo = ipatch_lookup_converter_info(0, G_OBJECT_TYPE(src), spec->value_type); if(!convinfo) { goto not_handled; } /* convert the src object to the link type and add to paste operation */ if(!ipatch_paste_object_add_convert(paste, convinfo->conv_type, src, IPATCH_CONTAINER(dest_base), &list, err)) /* ++ ref list */ { goto ret_err; } /* add the link operation */ ipatch_paste_object_link(paste, dest, IPATCH_ITEM(list->items->data)); g_object_unref(list); /* -- unref list */ goto ret_ok; /* we done */ } not_handled: g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNHANDLED_CONVERSION, _("Unhandled paste operation type '%s' => '%s'"), G_OBJECT_TYPE_NAME(src), G_OBJECT_TYPE_NAME(dest)); // Fall through ret_err: if(src_base) { g_object_unref(src_base); // -- unref } if(dest_base) { g_object_unref(dest_base); // -- unref } return (FALSE); ret_ok: if(src_base) { g_object_unref(src_base); // -- unref } if(dest_base) { g_object_unref(dest_base); // -- unref } return (TRUE); } libinstpatch-1.1.6/libinstpatch/IpatchPaste.h000066400000000000000000000146121400263525300212510ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_PASTE_H__ #define __IPATCH_PASTE_H__ #include #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchPaste IpatchPaste; typedef struct _IpatchPasteClass IpatchPasteClass; #define IPATCH_TYPE_PASTE (ipatch_paste_get_type ()) #define IPATCH_PASTE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_PASTE, IpatchPaste)) #define IPATCH_PASTE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_PASTE, IpatchPasteClass)) #define IPATCH_IS_PASTE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_PASTE)) #define IPATCH_IS_PASTE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_PASTE)) #define IPATCH_PASTE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_PASTE, IpatchPasteClass)) /* paste instance */ struct _IpatchPaste { GObject parent_instance; /* derived from GObject */ /* only available during a paste operation */ IpatchItem *dest; /* current destination */ /*< private >*/ GSList *add_list; /* list of AddItemBag operations */ GSList *add_list_last; /* last item in add_list for append optimization */ GHashTable *add_hash; /* hash of original -> AddItemBag (see IpatchPaste.c) */ GSList *link_list; /* list of LinkItemBag operations (in reverse order) */ }; /* conversion class */ struct _IpatchPasteClass { GObjectClass parent_class; }; /* choice values for when a item conflict occurs */ typedef enum { IPATCH_PASTE_CHOICE_IGNORE, /* item will be pasted (conflict remains) */ IPATCH_PASTE_CHOICE_REPLACE, /* replace item */ IPATCH_PASTE_CHOICE_KEEP, /* keep existing item (reverse replace) */ IPATCH_PASTE_CHOICE_CANCEL /* cancel the current operation */ } IpatchPasteChoice; /** * IpatchPasteTestFunc: * @dest: Destination item of paste operation * @src: Source item of paste operation * * Test if a paste handler can handle the paste operation. * * Returns: %TRUE if paste supported by this handler, %FALSE otherwise */ typedef gboolean(*IpatchPasteTestFunc)(IpatchItem *dest, IpatchItem *src); /** * IpatchPasteExecFunc: * @paste: Paste object * @dest: Destination item of paste operation * @src: Source item of paste operation * @err: Location to store error information * * Perform the construction phase of the paste operation. This includes every * action up to the point of actually adding/linking objects. All object * addition and linking operations are stored in @paste instance to be executed * after resolving conflicts, etc. * * Returns: %TRUE on success, %FALSE on error (in which case @err may be set). */ typedef gboolean(*IpatchPasteExecFunc)(IpatchPaste *paste, IpatchItem *dest, IpatchItem *src, GError **err); /** * IpatchPasteResolveFunc: * @paste: Paste instance * @conflict: Existing conflict item * @item: Conflicting item being pasted * * Function type used to handle paste item conflicts. * * Returns: Return a choice value for how the conflict should be handled. */ typedef IpatchPasteChoice(*IpatchPasteResolveFunc) (IpatchPaste *paste, IpatchItem *conflict, IpatchItem *item); /* priority levels for paste handlers */ typedef enum { /* 0 value is an alias for IPATCH_PASTE_PRIORITY_DEFAULT */ IPATCH_PASTE_PRIORITY_LOWEST = 1, IPATCH_PASTE_PRIORITY_LOW = 25, IPATCH_PASTE_PRIORITY_DEFAULT = 50, IPATCH_PASTE_PRIORITY_HIGH = 75, IPATCH_PASTE_PRIORITY_HIGHEST = 100 } IpatchPastePriority; #define IPATCH_PASTE_FLAGS_PRIORITY_MASK 0x7F void ipatch_register_paste_handler(IpatchPasteTestFunc test_func, IpatchPasteExecFunc exec_func, int flags); int ipatch_register_paste_handler_full(IpatchPasteTestFunc test_func, IpatchPasteExecFunc exec_func, GDestroyNotify notify_func, gpointer user_data, int flags); gboolean ipatch_is_paste_possible(IpatchItem *dest, IpatchItem *src); gboolean ipatch_simple_paste(IpatchItem *dest, IpatchItem *src, GError **err); GType ipatch_paste_get_type(void); IpatchPaste *ipatch_paste_new(void); gboolean ipatch_paste_objects(IpatchPaste *paste, IpatchItem *dest, IpatchItem *src, GError **err); gboolean ipatch_paste_resolve(IpatchPaste *paste, IpatchPasteResolveFunc resolve_func, gpointer user_data); gboolean ipatch_paste_finish(IpatchPaste *paste, GError **err); IpatchList *ipatch_paste_get_add_list(IpatchPaste *paste); void ipatch_paste_object_add(IpatchPaste *paste, IpatchItem *additem, IpatchContainer *parent, IpatchItem *orig); IpatchItem *ipatch_paste_object_add_duplicate(IpatchPaste *paste, IpatchItem *item, IpatchContainer *parent); IpatchItem *ipatch_paste_object_add_duplicate_deep(IpatchPaste *paste, IpatchItem *item, IpatchContainer *parent); gboolean ipatch_paste_object_add_convert(IpatchPaste *paste, GType conv_type, IpatchItem *item, IpatchContainer *parent, IpatchList **item_list, GError **err); void ipatch_paste_object_link(IpatchPaste *paste, IpatchItem *from, IpatchItem *to); gboolean ipatch_paste_default_test_func(IpatchItem *dest, IpatchItem *src); gboolean ipatch_paste_default_exec_func(IpatchPaste *paste, IpatchItem *dest, IpatchItem *src, GError **err); #endif libinstpatch-1.1.6/libinstpatch/IpatchRange.c000066400000000000000000000237621400263525300212320ustar00rootroot00000000000000/* * Swami * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchRange * @short_description: A boxed type which defines a number range * @see_also: * @stability: Stable * * Boxed type used for #GValue and #GParamSpec properties. Consists of a low * and a high integer value defining a range. */ #include "config.h" #include #include #include #include "IpatchRange.h" #include "IpatchXml.h" #include "IpatchXmlObject.h" #include "misc.h" #include "ipatch_priv.h" static gboolean ipatch_range_xml_encode_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err); static gboolean ipatch_range_xml_decode_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err); static void ipatch_param_spec_range_set_default(GParamSpec *pspec, GValue *value); static gboolean ipatch_param_spec_range_validate(GParamSpec *pspec, GValue *value); static gint ipatch_param_spec_range_values_cmp(GParamSpec *pspec, const GValue *value1, const GValue *value2); /** * _ipatch_range_init: (skip) * * Init function to register pickle XML encode/decode functions for ranges */ void _ipatch_range_init(void) { ipatch_xml_register_handler(IPATCH_TYPE_RANGE, NULL, ipatch_range_xml_encode_func, ipatch_range_xml_decode_func); } /* XML range value encoding function */ static gboolean ipatch_range_xml_encode_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err) { IpatchRange *range; g_return_val_if_fail(IPATCH_VALUE_HOLDS_RANGE(value), FALSE); range = g_value_get_boxed(value); if(range) { ipatch_xml_set_value_printf(node, "%d-%d", range->low, range->high); } return (TRUE); } static gboolean ipatch_range_xml_decode_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err) { IpatchRange *range; int low, high; const char *strval; strval = ipatch_xml_get_value(node); if(!strval) { g_value_set_boxed(value, NULL); } if(sscanf(strval, "%d-%d", &low, &high) != 2) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID, _("Invalid XML '%s' for range value"), strval); return (FALSE); } range = ipatch_range_new(low, high); g_value_take_boxed(value, range); return (TRUE); } GType ipatch_range_get_type(void) { static GType item_type = 0; if(!item_type) { item_type = g_boxed_type_register_static ("IpatchRange", (GBoxedCopyFunc) ipatch_range_copy, (GBoxedFreeFunc) ipatch_range_free); } return (item_type); } /** * ipatch_range_new: * @low: Low value to initialize range to * @high: High value to initialize range to * * Create a new value range structure (to store an integer range). * * Returns: Newly allocated integer range structure. */ IpatchRange * ipatch_range_new(int low, int high) { IpatchRange *range; range = g_slice_new(IpatchRange); range->low = low; range->high = high; return (range); } /** * ipatch_range_copy: * @range: Range structure to duplicate * * Duplicates an integer range structure. * * Returns: New duplicate range structure. */ IpatchRange * ipatch_range_copy(IpatchRange *range) { g_return_val_if_fail(range != NULL, NULL); return (ipatch_range_new(range->low, range->high)); } /** * ipatch_range_free: * @range: Integer range structure to free * * Free a range structure previously allocated with ipatch_range_new(). */ void ipatch_range_free(IpatchRange *range) { g_slice_free(IpatchRange, range); } /** * ipatch_value_set_range: * @value: a valid GValue of IPATCH_TYPE_RANGE boxed type * @range: Range structure to assign to @value * * Set the range values of a IPATCH_TYPE_RANGE GValue. The @range * structure is copied. */ void ipatch_value_set_range(GValue *value, const IpatchRange *range) { g_return_if_fail(IPATCH_VALUE_HOLDS_RANGE(value)); g_value_set_boxed(value, range); } /** * ipatch_value_set_static_range: (skip) * @value: A valid GValue of IPATCH_TYPE_RANGE boxed type * @range: (transfer full): Range structure to assign to @value * * Set the range values of a IPATCH_TYPE_RANGE GValue. This function uses * @range directly and so it should be static, use ipatch_value_set_range() * if the @range value should be duplicated. */ void ipatch_value_set_static_range(GValue *value, IpatchRange *range) { g_return_if_fail(IPATCH_VALUE_HOLDS_RANGE(value)); g_value_set_static_boxed(value, range); } /** * ipatch_value_get_range: * @value: A valid GValue of IPATCH_TYPE_RANGE boxed type * * Get the range structure from a IPATCH_TYPE_RANGE GValue. * * Returns: (transfer none): #IpatchRange structure containing the range values of @value or * %NULL if not set. The returned structure is NOT duplicated and is the * same pointer used in @value. */ IpatchRange * ipatch_value_get_range(const GValue *value) { g_return_val_if_fail(IPATCH_VALUE_HOLDS_RANGE(value), NULL); return ((IpatchRange *)g_value_get_boxed(value)); } GType ipatch_param_spec_range_get_type(void) { static GType spec_type = 0; if(!spec_type) { static const GParamSpecTypeInfo spec_info = { sizeof(IpatchParamSpecRange), 0, NULL, /* instance_init */ G_TYPE_BOXED, /* value type */ NULL, /* finalize */ ipatch_param_spec_range_set_default, ipatch_param_spec_range_validate, ipatch_param_spec_range_values_cmp, }; spec_type = g_param_type_register_static("IpatchParamSpecRange", &spec_info); } return (spec_type); } static void ipatch_param_spec_range_set_default(GParamSpec *pspec, GValue *value) { IpatchParamSpecRange *range_pspec = IPATCH_PARAM_SPEC_RANGE(pspec); IpatchRange *range; range = ipatch_value_get_range(value); if(!range) { range = ipatch_range_new(0, 0); ipatch_value_set_range(value, range); } range->low = range_pspec->default_low; range->high = range_pspec->default_high; } static gboolean ipatch_param_spec_range_validate(GParamSpec *pspec, GValue *value) { IpatchParamSpecRange *range_pspec = IPATCH_PARAM_SPEC_RANGE(pspec); IpatchRange *range, old_range; range = ipatch_value_get_range(value); if(!range) { range = ipatch_range_new(0, 0); range->low = range_pspec->default_low; range->high = range_pspec->default_high; return (TRUE); } old_range = *range; range->low = CLAMP(range->low, range_pspec->min, range_pspec->max); range->high = CLAMP(range->high, range_pspec->min, range_pspec->max); return (old_range.low != range->low || old_range.high != range->high); } static gint ipatch_param_spec_range_values_cmp(GParamSpec *pspec, const GValue *value1, const GValue *value2) { IpatchRange *range1, *range2; range1 = ipatch_value_get_range(value1); range2 = ipatch_value_get_range(value2); /* either one NULL? */ if(!range1 || !range2) { if(!range1 && !range2) { return 0; } if(!range1) { return -1; } return 1; } /* check if low value is not equal */ if(range1->low < range2->low) { return -1; } if(range1->low > range2->low) { return 1; } /* low values are equal */ /* check if high values are not equal */ if(range1->high < range2->high) { return -1; } if(range1->high > range2->high) { return 1; } /* range is equal */ return 0; } /** * ipatch_param_spec_range: * @name: Property name * @nick: Property nick name * @blurb: Property description blurb * @min: Minimum value for range end points (can be -1 to allow undefined * ranges) * @max: Maximum value for range end points * @default_low: Default value for low endpoint of range * @default_high: Default value for high endpoint of range * @flags: Property flags * * Create a parameter specification for IPATCH_TYPE_RANGE GValues. * * Returns: (transfer full): New range parameter specification. */ GParamSpec * ipatch_param_spec_range(const char *name, const char *nick, const char *blurb, int min, int max, int default_low, int default_high, GParamFlags flags) { IpatchParamSpecRange *range_spec; g_return_val_if_fail(min >= -1 && min <= max, NULL); g_return_val_if_fail(default_low >= min && default_low <= max, NULL); g_return_val_if_fail(default_high >= min && default_high <= max, NULL); /* FIXME - Whats the proper way to create a boxed GParamSpec? */ range_spec = g_param_spec_internal(IPATCH_TYPE_PARAM_RANGE, name, nick, blurb, flags); G_PARAM_SPEC(range_spec)->value_type = IPATCH_TYPE_RANGE; range_spec->min = min; range_spec->max = max; range_spec->default_low = default_low; range_spec->default_high = default_high; return ((GParamSpec *)range_spec); } libinstpatch-1.1.6/libinstpatch/IpatchRange.h000066400000000000000000000062661400263525300212370ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_RANGE_H__ #define __IPATCH_RANGE_H__ #include #include typedef struct _IpatchRange IpatchRange; typedef struct _IpatchParamSpecRange IpatchParamSpecRange; #define IPATCH_TYPE_RANGE (ipatch_range_get_type ()) #define IPATCH_VALUE_HOLDS_RANGE(value) \ (G_TYPE_CHECK_VALUE_TYPE ((value), IPATCH_TYPE_RANGE)) /* integer range structure */ struct _IpatchRange { int low; /* low endpoint of range or -1 if undefined */ int high; /* high endpoint of range or -1 if undefined */ }; /* set range value making sure that are in the correct order */ #define IPATCH_RANGE_SET_VALUES(range, val1, val2) G_STMT_START { \ if (val1 <= val2) \ { \ range.low = val1; \ range.high = val2; \ } \ else \ { \ range.low = val2; \ range.high = val1; \ } \ } G_STMT_END /* set a range value to a NULL range (an undefined value) */ #define IPATCH_RANGE_SET_NULL(range) G_STMT_START { \ range.low = range.high = -1; \ } G_STMT_END GType ipatch_range_get_type(void); IpatchRange *ipatch_range_new(int low, int high); IpatchRange *ipatch_range_copy(IpatchRange *range); void ipatch_range_free(IpatchRange *range); void ipatch_value_set_range(GValue *value, const IpatchRange *range); void ipatch_value_set_static_range(GValue *value, IpatchRange *range); IpatchRange *ipatch_value_get_range(const GValue *value); /* range parameter specification */ #define IPATCH_TYPE_PARAM_RANGE (ipatch_param_spec_range_get_type ()) #define IPATCH_IS_PARAM_SPEC_RANGE(pspec) \ (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), IPATCH_TYPE_PARAM_RANGE)) #define IPATCH_PARAM_SPEC_RANGE(pspec) \ (G_TYPE_CHECK_INSTANCE_CAST ((pspec), IPATCH_TYPE_PARAM_RANGE, \ IpatchParamSpecRange)) /* a parameter specification for the integer range type */ struct _IpatchParamSpecRange { GParamSpec parent_instance; /* derived from GParamSpec */ int min, max; /* min and max values for range endpoints */ int default_low, default_high; /* default vals for low and high endpoints */ }; GType ipatch_param_spec_range_get_type(void); GParamSpec *ipatch_param_spec_range(const char *name, const char *nick, const char *blurb, int min, int max, int default_low, int default_high, GParamFlags flags); #endif libinstpatch-1.1.6/libinstpatch/IpatchRiff.c000066400000000000000000001013551400263525300210570ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchRiff * @short_description: RIFF file parser/composer object * @see_also: * @stability: Stable * * A RIFF file parser/composer. Used for DLS, SoundFont and GigaSampler files. */ #include #include #include "IpatchRiff.h" #include "ipatch_priv.h" #include "i18n.h" static void ipatch_riff_finalize(GObject *obj); static void ipatch_riff_update_positions(IpatchRiff *riff); static gboolean verify_chunk_idstr(char idstr[4]); G_DEFINE_TYPE(IpatchRiff, ipatch_riff, G_TYPE_OBJECT) static void ipatch_riff_class_init(IpatchRiffClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->finalize = ipatch_riff_finalize; } static void ipatch_riff_init(IpatchRiff *riff) { riff->status = IPATCH_RIFF_STATUS_BEGIN; riff->mode = IPATCH_RIFF_READ; riff->flags = 0; riff->handle = NULL; riff->chunks = g_array_new(FALSE, FALSE, sizeof(IpatchRiffChunk)); riff->state_stack = NULL; } static void ipatch_riff_finalize(GObject *obj) { IpatchRiff *riff = IPATCH_RIFF(obj); GList *p; if(riff->handle) { ipatch_file_close(riff->handle); /* -- unref file object */ } g_array_free(riff->chunks, TRUE); for(p = riff->state_stack; p; p = g_list_next(p)) { g_array_free((GArray *)(p->data), TRUE); } if(G_OBJECT_CLASS(ipatch_riff_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_riff_parent_class)->finalize(obj); } } /** * ipatch_riff_error_quark: (skip) */ GQuark ipatch_riff_error_quark(void) { static GQuark q = 0; if(q == 0) { q = g_quark_from_static_string("riff-error-quark"); } return (q); } /** * ipatch_riff_new: * @handle: File object handle to parse or %NULL to set later, the handle will be * taken over by the riff object and closed when finalized. * * Create a new RIFF file riff/composer object * * Returns: The RIFF object */ IpatchRiff * ipatch_riff_new(IpatchFileHandle *handle) { IpatchRiff *riff; g_return_val_if_fail(!handle || IPATCH_IS_FILE_HANDLE(handle), NULL); riff = g_object_new(IPATCH_TYPE_RIFF, NULL); if(handle) { ipatch_riff_set_file_handle(riff, handle); } return (riff); } /** * ipatch_riff_set_file_handle: * @riff: RIFF object * @handle: File object handle to assign * * Set the file object handle of a RIFF object. The handle is taken over * by the riff object and will be closed when finalized. */ void ipatch_riff_set_file_handle(IpatchRiff *riff, IpatchFileHandle *handle) { g_return_if_fail(IPATCH_IS_RIFF(riff)); g_return_if_fail(IPATCH_IS_FILE_HANDLE(handle)); g_array_set_size(riff->chunks, 0); /* reset chunk state */ /* Close old handle, if any */ if(riff->handle) { ipatch_file_close(riff->handle); } riff->handle = handle; } /** * ipatch_riff_get_file: * @riff: RIFF object * * Get the file handle from a RIFF object. * * Returns: The file handle or %NULL if not assigned. */ IpatchFileHandle * ipatch_riff_get_file_handle(IpatchRiff *riff) { g_return_val_if_fail(IPATCH_IS_RIFF(riff), NULL); return (riff->handle); } /** * ipatch_riff_get_chunk_level: * @riff: RIFF object * * Gets the current chunk level count (number of embedded chunks) currently * being processed in a RIFF file. * * Returns: Chunk level count (0 = no open chunks) */ int ipatch_riff_get_chunk_level(IpatchRiff *riff) { g_return_val_if_fail(IPATCH_IS_RIFF(riff), 0); return (riff->chunks->len); } /** * ipatch_riff_get_chunk_array: * @riff: RIFF object * @count: (out): Location to store the number of elements in the returned array * * Gets the array of open chunk info structures. * * Returns: (array length=count) (transfer none): Array of #IpatchRiffChunk * structures or %NULL if no chunks being processed (processing hasn't started). * The returned array is internal and should not be modified or freed. Also note that the * array is valid only while the chunk state is unchanged (riff object or file * operations). The number of elements in the array is stored in @count. */ IpatchRiffChunk * ipatch_riff_get_chunk_array(IpatchRiff *riff, int *count) { if(count) { *count = 0; /* in case of error */ } g_return_val_if_fail(IPATCH_IS_RIFF(riff), NULL); /* Update the chunk positions */ ipatch_riff_update_positions(riff); if(count) { *count = riff->chunks->len; } if(riff->chunks->len > 0) { return (&g_array_index(riff->chunks, IpatchRiffChunk, 0)); } else { return (NULL); } } /** * ipatch_riff_get_chunk: * @riff: RIFF object * @level: Level of chunk to get (-1 for current chunk) * * Get the chunk at the specified @level from a RIFF @riff chunk state * array. * * Returns: (transfer none): Pointer to the chunk or %NULL if invalid level. The returned * chunk is internal and should not be modified or freed. Also note that the * chunk is valid only while the chunk state is unchanged (riff object or file * operations). */ IpatchRiffChunk * ipatch_riff_get_chunk(IpatchRiff *riff, int level) { int chunks_len; g_return_val_if_fail(IPATCH_IS_RIFF(riff), NULL); g_return_val_if_fail(riff->chunks->len > 0, NULL); /* Update the chunk positions */ ipatch_riff_update_positions(riff); chunks_len = (int)riff->chunks->len; if(level == -1) { level = chunks_len - 1; } g_return_val_if_fail(level >= -1 && level < chunks_len, NULL); return (&g_array_index(riff->chunks, IpatchRiffChunk, level)); } /** * ipatch_riff_get_total_size: * @riff: RIFF object * * Get total size of toplevel chunk. This is a convenience function * that just adds the size of the toplevel chunk and its header, the * actual file object size is not checked. * * Returns: Size of toplevel chunk + header size, in bytes. Actual file size * is not checked. */ guint32 ipatch_riff_get_total_size(IpatchRiff *riff) { IpatchRiffChunk *chunk; g_return_val_if_fail(IPATCH_IS_RIFF(riff), 0); /* Update the chunk positions */ ipatch_riff_update_positions(riff); if(riff->chunks->len == 0) { return (0); } chunk = &g_array_index(riff->chunks, IpatchRiffChunk, 0); return (chunk->size + IPATCH_RIFF_HEADER_SIZE); } /** * ipatch_riff_get_position: * @riff: RIFF object * * Get current position in the toplevel RIFF chunk (including header, * i.e., the file position). * * Returns: The current offset, in bytes, into the toplevel RIFF chunk * (including header). */ guint32 ipatch_riff_get_position(IpatchRiff *riff) { IpatchRiffChunk *chunk; g_return_val_if_fail(IPATCH_IS_RIFF(riff), 0); /* Update the chunk positions */ ipatch_riff_update_positions(riff); if(riff->chunks->len == 0) { return (0); } chunk = &g_array_index(riff->chunks, IpatchRiffChunk, 0); return (chunk->position + IPATCH_RIFF_HEADER_SIZE); } /** * ipatch_riff_push_state: * @riff: RIFF object * * Pushes the current file position and chunk state onto the state stack. * This state can be later restored to return to the same position in a RIFF * file. */ void ipatch_riff_push_state(IpatchRiff *riff) { GArray *dup_array; g_return_if_fail(IPATCH_IS_RIFF(riff)); /* Update the chunk positions */ ipatch_riff_update_positions(riff); dup_array = g_array_new(FALSE, FALSE, sizeof(IpatchRiffChunk)); if(riff->chunks->len > 0) { g_array_append_vals(dup_array, riff->chunks->data, riff->chunks->len); } riff->state_stack = g_list_prepend(riff->state_stack, dup_array); } /** * ipatch_riff_pop_state: * @riff: RIFF object * @err: Location to store error info or %NULL * * Pops the most recent state pushed onto the state stack. This causes the * position in the RIFF file stored by the state to be restored. * * Returns: %TRUE on success, %FALSE otherwise which is fatal */ gboolean ipatch_riff_pop_state(IpatchRiff *riff, GError **err) { IpatchRiffChunk *chunk; gboolean retval; guint pos; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(riff->state_stack != NULL, FALSE); g_array_free(riff->chunks, TRUE); riff->chunks = riff->state_stack->data; riff->state_stack = g_list_delete_link(riff->state_stack, riff->state_stack); /* We want the current chunk state position, not the current file position * which we would get from ipatch_riff_get_position() */ if(riff->chunks->len > 0) { chunk = &g_array_index(riff->chunks, IpatchRiffChunk, 0); pos = chunk->position + IPATCH_RIFF_HEADER_SIZE; } else { pos = 0; } retval = ipatch_file_seek(riff->handle, pos, G_SEEK_SET, err); return (retval); } /* initializes riff object to default state */ static void ipatch_riff_reset(IpatchRiff *riff) { riff->status = IPATCH_RIFF_STATUS_BEGIN; riff->mode = IPATCH_RIFF_READ; riff->flags = 0; g_array_set_size(riff->chunks, 0); } /** * ipatch_riff_start_read: * @riff: RIFF object * @err: Location to store error info or %NULL * * Start parsing the @riff file object as if it were at the * beginning of a RIFF file. Clears any current chunk state, * loads a chunk and ensures that it has the "RIFF" or "RIFX" ID. If this call * is sucessful there will be one chunk on the chunk stack with the * secondary ID of the RIFF chunk. If it is desirable to process a * chunk that is not the beginning of a RIFF file, * ipatch_riff_start_read_chunk() can be used. This function will also * automatically enable byte order swapping if needed. * * Returns: (transfer none): Pointer to the opened RIFF chunk or %NULL on error. The returned * chunk is internal and should not be modified or freed. Also note that the * chunk is valid only while the chunk state is unchanged (riff object or file * operations). */ IpatchRiffChunk * ipatch_riff_start_read(IpatchRiff *riff, GError **err) { IpatchRiffChunk *chunk; g_return_val_if_fail(IPATCH_IS_RIFF(riff), NULL); g_return_val_if_fail(riff->status != IPATCH_RIFF_STATUS_FAIL, NULL); g_return_val_if_fail(!err || !*err, NULL); ipatch_riff_reset(riff); riff->mode = IPATCH_RIFF_READ; if(!(chunk = ipatch_riff_read_chunk(riff, err))) { return (NULL); } if(chunk->type != IPATCH_RIFF_CHUNK_RIFF) { g_array_set_size(riff->chunks, 0); /* clear non "RIFF" chunk */ riff->status = IPATCH_RIFF_STATUS_FAIL; g_set_error(&riff->err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_NOT_RIFF, _("Not a RIFF file")); if(err) { *err = g_error_copy(riff->err); } return (NULL); } return (chunk); } /** * ipatch_riff_start_read_chunk: * @riff: RIFF object * @err: Location to store error info or %NULL * * Start parsing the @riff file object at an arbitrary chunk. * Clears any current chunk state and loads a chunk. If this * call is sucessful there will be one chunk on the chunk stack. If it * is desirable to start processing from the beginning of a RIFF file * ipatch_riff_start_read() should be used instead. An end of file * condition is considered an error. Note that it is up to the caller to * ensure byte order swapping is enabled, if needed. * * Returns: (transfer none): Pointer to the opened RIFF chunk or %NULL on error. The returned * chunk is internal and should not be modified or freed. Also note that the * chunk is valid only while the chunk state is unchanged (riff object or file * operations). */ IpatchRiffChunk * ipatch_riff_start_read_chunk(IpatchRiff *riff, GError **err) { IpatchRiffChunk *chunk; g_return_val_if_fail(IPATCH_IS_RIFF(riff), NULL); g_return_val_if_fail(riff->status != IPATCH_RIFF_STATUS_FAIL, NULL); g_return_val_if_fail(!err || !*err, NULL); ipatch_riff_reset(riff); riff->mode = IPATCH_RIFF_READ; chunk = ipatch_riff_read_chunk(riff, err); return (chunk); } /** * ipatch_riff_read_chunk: * @riff: RIFF object * @err: Location to store error info or %NULL * * Parse next RIFF chunk header. The ipatch_riff_close_chunk() * function should be called at the end of parsing a chunk, otherwise this * function will return NULL if the current chunk has ended. When * the first RIFF chunk is read the IPATCH_RIFF_FLAG_BIG_ENDIAN flag * is cleared or set depending on if its RIFF or RIFX respectively, * endian swapping is also enabled if the file uses non-native endian * format to the host. * * Returns: (transfer none): Pointer to new opened chunk or %NULL if current chunk has ended * or on error. Returned chunk pointer is internal and should not be modified * or freed. Also note that the chunk is valid only while the chunk state is * unchanged (riff object or file operations). */ IpatchRiffChunk * ipatch_riff_read_chunk(IpatchRiff *riff, GError **err) { IpatchRiffChunk *chunk; IpatchRiffChunk newchunk; guint32 buf[IPATCH_RIFF_HEADER_SIZE / 4]; guint size; guint32 id; int i, c; g_return_val_if_fail(IPATCH_IS_RIFF(riff), NULL); g_return_val_if_fail(riff->status != IPATCH_RIFF_STATUS_FAIL, NULL); g_return_val_if_fail(riff->mode == IPATCH_RIFF_READ, NULL); g_return_val_if_fail(riff->handle != NULL, NULL); g_return_val_if_fail(!err || !*err, NULL); /* return finished if we already finished */ if(riff->status == IPATCH_RIFF_STATUS_FINISHED || riff->status == IPATCH_RIFF_STATUS_CHUNK_END) { return (NULL); } if(riff->chunks->len > 0) { guint32 chunk_position; /* Update the chunk positions */ ipatch_riff_update_positions(riff); chunk = &g_array_index(riff->chunks, IpatchRiffChunk, riff->chunks->len - 1); chunk_position = (guint32)chunk->position; /* current chunk is sub chunk, or pos past end? */ if(chunk->type == IPATCH_RIFF_CHUNK_SUB || chunk_position >= chunk->size) { riff->status = IPATCH_RIFF_STATUS_CHUNK_END; return (NULL); } } /* read FOURCC ID and chunk size */ if(!ipatch_file_read(riff->handle, buf, IPATCH_RIFF_HEADER_SIZE, &riff->err)) { riff->status = IPATCH_RIFF_STATUS_FAIL; if(err) { *err = g_error_copy(riff->err); } return (NULL); } id = buf[0]; /* unexpected "RIFF" chunk? */ if(id == IPATCH_FOURCC_RIFF && riff->chunks->len > 0) { riff->status = IPATCH_RIFF_STATUS_FAIL; g_set_error(&riff->err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_UNEXPECTED_ID, _("Unexpected 'RIFF' chunk")); if(err) { *err = g_error_copy(riff->err); } return (NULL); } /* Position of chunk data (or LIST secondary chunk ID) */ newchunk.filepos = ipatch_file_get_position(riff->handle); /* is a list chunk (LIST or RIFF)? */ if(id == IPATCH_FOURCC_LIST || id == IPATCH_FOURCC_RIFF || id == IPATCH_FOURCC_RIFX) { if(id == IPATCH_FOURCC_LIST) { newchunk.type = IPATCH_RIFF_CHUNK_LIST; } else if(id == IPATCH_FOURCC_RIFF) { newchunk.type = IPATCH_RIFF_CHUNK_RIFF; ipatch_file_set_little_endian(riff->handle->file); } else /* RIFX big endian chunk? */ { newchunk.type = IPATCH_RIFF_CHUNK_RIFF; ipatch_file_set_big_endian(riff->handle->file); } /* read secondary chunk ID over old ID */ if(!ipatch_file_read(riff->handle, &buf, IPATCH_RIFF_FOURCC_SIZE, &riff->err)) { riff->status = IPATCH_RIFF_STATUS_FAIL; if(err) { *err = g_error_copy(riff->err); } return (NULL); } newchunk.position = 4; } else /* sub chunk */ { newchunk.type = IPATCH_RIFF_CHUNK_SUB; newchunk.position = 0; } newchunk.id = buf[0]; memcpy(&newchunk.idstr, &newchunk.id, 4); if(!verify_chunk_idstr(newchunk.idstr)) { riff->status = IPATCH_RIFF_STATUS_FAIL; g_set_error(&riff->err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_INVALID_ID, _("Invalid RIFF chunk id")); if(err) { *err = g_error_copy(riff->err); } return (NULL); } newchunk.size = IPATCH_FILE_SWAP32(riff->handle->file, buf + 1); /* list chunk size should be even (sub chunks can be odd) */ if(newchunk.type != IPATCH_RIFF_CHUNK_SUB && newchunk.size % 2) { riff->status = IPATCH_RIFF_STATUS_FAIL; g_set_error(&riff->err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_ODD_SIZE, _("Invalid RIFF LIST chunk size (odd number)")); if(err) { *err = g_error_copy(riff->err); } return (NULL); } size = (newchunk.size + 1) & ~1; /* round up to even if odd size */ /* Update the chunk positions */ ipatch_riff_update_positions(riff); /* make sure chunk size does not exceed its parent sizes */ c = riff->chunks->len; for(i = 0; i < c; i++) { chunk = &g_array_index(riff->chunks, IpatchRiffChunk, i); if(chunk->position + size - newchunk.position > chunk->size) { riff->status = IPATCH_RIFF_STATUS_FAIL; g_set_error(&riff->err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_SIZE_EXCEEDED, _("Child chunk '%.4s' (size = %d, level = %d) exceeds" " parent chunk '%.4s' (size = %d, level = %d)"), newchunk.idstr, newchunk.size, c, chunk->idstr, chunk->size, i); if(err) { *err = g_error_copy(riff->err); } return (NULL); } } g_array_append_val(riff->chunks, newchunk); riff->status = IPATCH_RIFF_STATUS_NORMAL; return ((IpatchRiffChunk *)&g_array_index(riff->chunks, IpatchRiffChunk, riff->chunks->len - 1)); } /* Update all open chunk positions (called after file position changes) */ static void ipatch_riff_update_positions(IpatchRiff *riff) { IpatchRiffChunk *chunk; gint32 filepos; int size, i; size = riff->chunks->len; if(size == 0) { return; } filepos = ipatch_file_get_position(riff->handle); for(i = 0; i < size; i++) { chunk = &g_array_index(riff->chunks, IpatchRiffChunk, i); chunk->position = filepos - chunk->filepos; } } /** * ipatch_riff_read_chunk_verify: * @riff: RIFF object * @type: Expected chunk type * @id: Expected chunk ID * @err: Location to store error info or %NULL * * Like ipatch_riff_read_chunk() but ensures that the new chunk matches a * specific type and ID. If the chunk is not the expected chunk or no more * chunks in current list chunk, it is considered an error. * ipatch_riff_close_chunk() should be called when finished parsing the * opened chunk. * * Returns: (transfer none): Pointer to new opened chunk or %NULL if current chunk has ended * or on error. Returned chunk pointer is internal and should not be modified * or freed. Also note that the chunk is valid only while the chunk state is * unchanged (riff object or file operations). */ IpatchRiffChunk * ipatch_riff_read_chunk_verify(IpatchRiff *riff, IpatchRiffChunkType type, guint32 id, GError **err) { IpatchRiffChunk *chunk; char *idstr; g_return_val_if_fail(IPATCH_IS_RIFF(riff), NULL); g_return_val_if_fail(riff->status != IPATCH_RIFF_STATUS_FAIL, NULL); g_return_val_if_fail(type >= IPATCH_RIFF_CHUNK_RIFF && type <= IPATCH_RIFF_CHUNK_SUB, NULL); idstr = (char *)(&id); g_return_val_if_fail(verify_chunk_idstr(idstr), NULL); g_return_val_if_fail(!err || !*err, NULL); if(!(chunk = ipatch_riff_read_chunk(riff, &riff->err))) { if(!riff->err) g_set_error(&riff->err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_UNEXPECTED_CHUNK_END, _("Unexpected end of LIST while looking for chunk '%.4s'"), idstr); if(err) { *err = g_error_copy(riff->err); } return (NULL); } if(chunk->type != type || chunk->id != id) { riff->status = IPATCH_RIFF_STATUS_FAIL; g_set_error(&riff->err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_UNEXPECTED_ID, _("Unexpected RIFF chunk with ID '%.4s' (expected '%.4s')"), chunk->idstr, idstr); if(err) { *err = g_error_copy(riff->err); } return (NULL); } return (chunk); } /* verify the characters of a chunk ID string */ static gboolean verify_chunk_idstr(char idstr[4]) { int i; char c; for(i = 0; i < 4; i++) /* chars of FOURCC should be alphanumeric */ { c = idstr[i]; if(!(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') && !(c >= '0' && c <= '9')) { break; } } if(i < 4 && c == ' ' && i > 0) /* can pad with spaces (at least 1 char) */ do { i++; } while(i < 4 && idstr[i] == ' '); return (i == 4); } /** * ipatch_riff_write_chunk: * @riff: RIFF object * @type: Chunk type (RIFF, LIST, or SUB) * @id: Chunk ID (secondary ID for RIFF and LIST chunks) * @err: Location to store error info or %NULL * * Opens a new chunk and writes a chunk header to the file object in @riff. * The size field of the chunk is set to 0 and will be filled in when the * chunk is closed (see ipatch_riff_close_chunk()). * * Returns: %TRUE on success, %FALSE otherwise. */ gboolean ipatch_riff_write_chunk(IpatchRiff *riff, IpatchRiffChunkType type, guint32 id, GError **err) { IpatchRiffChunk chunk; guint32 buf[3]; char *idstr; int size; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(riff->status != IPATCH_RIFF_STATUS_FAIL, FALSE); g_return_val_if_fail(type >= IPATCH_RIFF_CHUNK_RIFF && type <= IPATCH_RIFF_CHUNK_SUB, FALSE); idstr = (char *)(&id); g_return_val_if_fail(verify_chunk_idstr(idstr), FALSE); g_return_val_if_fail(!err || !*err, FALSE); riff->mode = IPATCH_RIFF_WRITE; buf[1] = 0; /* set chunk size to 0 (will seek back later) */ if(type == IPATCH_RIFF_CHUNK_LIST || type == IPATCH_RIFF_CHUNK_RIFF) { if(type == IPATCH_RIFF_CHUNK_LIST) { buf[0] = IPATCH_FOURCC_LIST; } else { buf[0] = IPATCH_RIFF_BIG_ENDIAN(riff) ? IPATCH_FOURCC_RIFX : IPATCH_FOURCC_RIFF; } buf[2] = id; /* set secondary list chunk ID */ chunk.position = chunk.size = 4; size = 12; } else { buf[0] = id; /* set sub chunk ID */ chunk.position = chunk.size = 0; size = 8; } if(!ipatch_file_write(riff->handle, buf, size, &riff->err)) { riff->status = IPATCH_RIFF_STATUS_FAIL; if(err) { *err = g_error_copy(riff->err); } return (FALSE); } /* Update the chunk positions */ ipatch_riff_update_positions(riff); chunk.type = type; chunk.id = id; memcpy(&chunk.idstr, &id, 4); chunk.filepos = ipatch_file_get_position(riff->handle) - chunk.position; g_array_append_val(riff->chunks, chunk); return (TRUE); } /** * ipatch_riff_close_chunk: * @riff: RIFF object * @level: Level of chunk to close (-1 for current chunk) * @err: Location to store error info or %NULL * * Closes the chunk specified by @level and all its children (if any). * * In write mode the chunk size is filled in for chunks that get closed and * therefore the file object of @riff must be seekable (anyone need * non-seekable RIFF writing?). The chunk size is padded to an even * number if necessary (by writing a NULL byte). * * Upon successful completion the file position will be where it was prior to * the call (write mode) or at the beginning of the next chunk (read mode). * There will be @level open chunks (or previous chunk count - 1 if * @level == -1). In read mode the status will be * #IPATCH_RIFF_STATUS_NORMAL if open chunks remain or * #IPATCH_RIFF_STATUS_FINISHED if toplevel chunk was closed. The status is * not modified in write mode. * * Returns: %TRUE on success, %FALSE otherwise. */ gboolean ipatch_riff_close_chunk(IpatchRiff *riff, int level, GError **err) { IpatchRiffChunk *chunk; char nul = '\0'; /* null byte to pad odd sized chunks */ gint32 offset = 0; /* current offset from original position */ gint32 seek; guint32 size; int retval = TRUE; int i, chunks_len; g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(riff->status != IPATCH_RIFF_STATUS_FAIL, FALSE); g_return_val_if_fail(riff->chunks->len > 0, FALSE); g_return_val_if_fail(!err || !*err, FALSE); chunks_len = (int)riff->chunks->len; if(level == -1) { level = chunks_len - 1; } g_return_val_if_fail(level >= -1 && level < chunks_len, FALSE); /* Update the chunk positions */ ipatch_riff_update_positions(riff); if(riff->mode == IPATCH_RIFF_READ) /* read mode? */ { chunk = &g_array_index(riff->chunks, IpatchRiffChunk, level); /* round odd chunk sizes to even */ seek = ((chunk->size + 1) & ~1) - chunk->position; /* close all chunks below and including level */ g_array_set_size(riff->chunks, level); if(seek != 0) { /* seek to the end of the specified chunk */ if(!ipatch_file_seek(riff->handle, seek, G_SEEK_CUR, &riff->err)) { riff->status = IPATCH_RIFF_STATUS_FAIL; if(err) { *err = g_error_copy(riff->err); } return (FALSE); } /* Update the chunk positions */ ipatch_riff_update_positions(riff); } if(level > 0) { riff->status = IPATCH_RIFF_STATUS_NORMAL; } else { riff->status = IPATCH_RIFF_STATUS_FINISHED; } return (TRUE); } else /* write mode */ { for(i = riff->chunks->len - 1; i >= level; i--) { chunk = &g_array_index(riff->chunks, IpatchRiffChunk, i); /* make sure we don't have a negative chunk size! */ if(log_if_fail(chunk->position >= 0)) { goto fail; } /* we don't include padding (if any) in the size */ size = chunk->position; if(chunk->position % 2) /* need pad to even chunk size? */ { int i2; if(!ipatch_file_write(riff->handle, &nul, 1, &riff->err)) { goto fail; } /* add pad byte to chunk positions */ for(i2 = i; i2 >= 0; i2--) { g_array_index(riff->chunks, IpatchRiffChunk, i2).position++; } } /* seek to the chunk size field */ seek = -chunk->position - 4 - offset; if(seek != 0) if(!ipatch_file_seek(riff->handle, seek, G_SEEK_CUR, &riff->err)) { goto fail; } offset += seek; /* write the chunk size */ if(!ipatch_file_write_u32(riff->handle, size, &riff->err)) { goto fail; } offset += 4; } g_array_set_size(riff->chunks, level); /* close chunk(s) */ ret: /* return to the original position */ if(offset && !ipatch_file_seek(riff->handle, -offset, G_SEEK_CUR, retval ? err : NULL)) { riff->status = IPATCH_RIFF_STATUS_FAIL; retval = FALSE; } if(!retval && riff->err && err) { *err = g_error_copy(riff->err); } return (retval); fail: riff->status = IPATCH_RIFF_STATUS_FAIL; retval = FALSE; goto ret; } /* else - (write mode) */ } /** * ipatch_riff_skip_chunks: * @riff: RIFF object * @count: Number of chunks to skip * @err: Location to store error info or %NULL * * Skips RIFF chunks at the current chunk level (children of the current * chunk). * * Returns: %TRUE on success, %FALSE otherwise */ gboolean ipatch_riff_skip_chunks(IpatchRiff *riff, guint count, GError **err) { guint i; for(i = 0; i < count; i++) { if(!ipatch_riff_read_chunk(riff, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } } return (TRUE); } /** * ipatch_riff_get_error: * @riff: RIFF object * @err: Location to store error info * * Gets error information from a RIFF object. * * Returns: %FALSE if status is a #IPATCH_RIFF_STATUS_FAIL condition and info * can be found in @err, %TRUE if no error has occured. */ gboolean ipatch_riff_get_error(IpatchRiff *riff, GError **err) { g_return_val_if_fail(IPATCH_IS_RIFF(riff), FALSE); g_return_val_if_fail(!err || !*err, FALSE); if(riff->status != IPATCH_RIFF_STATUS_FAIL) { return (TRUE); } if(err) { *err = g_error_copy(riff->err); } return (FALSE); } /** * ipatch_riff_message_detail: * @riff: RIFF object * @level: Chunk level to generate detail for (-1 for current chunk) * @format: Printf style format string of message to display at beginning * of riff detail * @...: Arguments for @msg string * * Generates a detailed message, including current position in RIFF file * and a chunk trace back. Useful for debugging purposes. * * Returns: (transfer none): Detailed message string which is internal to @riff and should * not be modified or freed. Also note that this string is only valid until * the next call to this function. */ char * ipatch_riff_message_detail(IpatchRiff *riff, int level, const char *format, ...) { va_list args; IpatchRiffChunk *chunk; char *msg, *debug, *traceback = NULL, *s, *s2; int i, riffchunkpos = 0; int chunks_len; g_return_val_if_fail(IPATCH_IS_RIFF(riff), NULL); /* Update the chunk positions */ ipatch_riff_update_positions(riff); /* level will be -1 if already -1 and no chunks */ chunks_len = (int)riff->chunks->len; if(level == -1) { level = chunks_len - 1; } g_return_val_if_fail(level >= -1 && level < chunks_len, NULL); va_start(args, format); msg = g_strdup_vprintf(format, args); va_end(args); if(riff->chunks->len > 0) { chunk = &g_array_index(riff->chunks, IpatchRiffChunk, 0); riffchunkpos = chunk->position; } debug = g_strdup_printf(" (ofs=%x, traceback [", riffchunkpos); if(riff->chunks->len > 0) { i = level; while(i >= 0) { chunk = &g_array_index(riff->chunks, IpatchRiffChunk, i); s = g_strdup_printf("'%.4s' ofs=0x%X, size=%d%s", chunk->idstr, riffchunkpos - chunk->position, chunk->size, i != 0 ? " <= " : ""); if(traceback) { s2 = g_strconcat(traceback, s, NULL); g_free(s); g_free(traceback); traceback = s2; } else { traceback = s; } i--; } } else { traceback = g_strdup(""); } s = g_strconcat(msg, debug, traceback, "])", NULL); g_free(msg); g_free(debug); g_free(traceback); g_free(riff->msg_detail); riff->msg_detail = s; return (s); } libinstpatch-1.1.6/libinstpatch/IpatchRiff.h000066400000000000000000000163211400263525300210620ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_RIFF_H__ #define __IPATCH_RIFF_H__ #include #include typedef struct _IpatchRiff IpatchRiff; typedef struct _IpatchRiffClass IpatchRiffClass; typedef struct _IpatchRiffChunk IpatchRiffChunk; #define IPATCH_TYPE_RIFF (ipatch_riff_get_type ()) #define IPATCH_RIFF(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_RIFF, IpatchRiff)) #define IPATCH_RIFF_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_RIFF, IpatchRiffClass)) #define IPATCH_IS_RIFF(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_RIFF)) #define IPATCH_IS_RIFF_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_RIFF)) typedef enum { IPATCH_RIFF_STATUS_FAIL = 0, /* error occured */ IPATCH_RIFF_STATUS_BEGIN, /* parsing has not yet began */ IPATCH_RIFF_STATUS_FINISHED, /* no more parsing to be done */ IPATCH_RIFF_STATUS_NORMAL, /* normal status */ IPATCH_RIFF_STATUS_CHUNK_END /* end of a chunk */ } IpatchRiffStatus; typedef enum { IPATCH_RIFF_READ, IPATCH_RIFF_WRITE } IpatchRiffMode; /* RIFF object */ struct _IpatchRiff { GObject parent_instance; /* derived from GObject */ IpatchRiffStatus status; /* current status */ IpatchRiffMode mode; /* I/O mode (read/write) */ guint flags; /* some flags */ IpatchFileHandle *handle; /* file object being parsed */ GError *err; /* error information if status == IPATCH_RIFF_FAIL */ char *msg_detail; /* last generated message detail string */ GArray *chunks; /* chunk trail (IpatchRiffChunk structures) */ GList *state_stack; /* saved states (positions) */ }; /* RIFF class */ struct _IpatchRiffClass { GObjectClass parent_class; }; #define IPATCH_RIFF_NEED_SWAP(riff) IPATCH_FILE_NEED_SWAP (riff->handle->file) #define IPATCH_RIFF_BIG_ENDIAN(riff) IPATCH_FILE_BIG_ENDIAN (riff->handle->file) typedef enum { IPATCH_RIFF_CHUNK_RIFF, /* toplevel "RIFF" (or "RIFX") list chunk */ IPATCH_RIFF_CHUNK_LIST, /* a "LIST" chunk */ IPATCH_RIFF_CHUNK_SUB /* a sub chunk */ } IpatchRiffChunkType; /* structure describing a RIFF chunk */ struct _IpatchRiffChunk { IpatchRiffChunkType type; /* type of chunk */ guint32 id; /* chunk ID in integer format for easy comparison */ char idstr[4]; /* four character chunk ID string */ gint32 position; /* current position in chunk (read or write mode) */ guint32 size; /* size of chunk (read mode only) */ guint32 filepos; /* Position in file object of chunk data */ }; /* error domain for g_set_error */ #define IPATCH_RIFF_ERROR ipatch_riff_error_quark() typedef enum { IPATCH_RIFF_ERROR_NOT_RIFF, /* not a RIFF file */ IPATCH_RIFF_ERROR_UNEXPECTED_ID, /* unexpected chunk ID */ IPATCH_RIFF_ERROR_UNEXPECTED_CHUNK_END, /* unexpected LIST chunk end */ IPATCH_RIFF_ERROR_INVALID_ID, /* invalid chunk FOURCC ID */ IPATCH_RIFF_ERROR_ODD_SIZE, /* chunk size is ODD */ IPATCH_RIFF_ERROR_SIZE_EXCEEDED, /* chunk size exceeded */ /* convenience errors - not used by the riff object itself */ IPATCH_RIFF_ERROR_SIZE_MISMATCH, /* chunk size mismatch */ IPATCH_RIFF_ERROR_INVALID_DATA /* generic invalid data error */ } IpatchRiffError; /* macro to convert 4 char RIFF ids to a guint32 integer for comparisons */ #if G_BYTE_ORDER == G_LITTLE_ENDIAN #define IPATCH_FOURCC(c1, c2, c3, c4) \ ((guint32)(c1) | ((guint32)(c2) << 8) | ((guint32)(c3) << 16) \ | ((guint32)(c4) << 24)) #elif G_BYTE_ORDER == G_BIG_ENDIAN #define IPATCH_FOURCC(c1, c2, c3, c4) \ ((guint32)(c4) | ((guint32)(c3) << 8) | ((guint32)(c2) << 16) \ | ((guint32)(c1) << 24)) #else #error Processor not big or little endian? #endif /* RIFF chunk FOURCC guint32 integers */ #define IPATCH_FOURCC_RIFF IPATCH_FOURCC ('R','I','F','F') #define IPATCH_FOURCC_RIFX IPATCH_FOURCC ('R','I','F','X') /* big endian */ #define IPATCH_FOURCC_LIST IPATCH_FOURCC ('L','I','S','T') /** * IPATCH_RIFF_HEADER_SIZE: (skip) */ #define IPATCH_RIFF_HEADER_SIZE 8 /* size of RIFF chunk headers (ID + size) */ /** * IPATCH_RIFF_FOURCC_SIZE: (skip) */ #define IPATCH_RIFF_FOURCC_SIZE 4 /* RIFF FOURCC ID size */ /** * IPATCH_RIFF_LIST_HEADER_SIZE: (skip) */ /* chunk header + 4 character list type */ #define IPATCH_RIFF_LIST_HEADER_SIZE \ (IPATCH_RIFF_HEADER_SIZE + IPATCH_RIFF_FOURCC_SIZE) /* some RIFF WAVE file specific defines */ /** * IPATCH_RIFF_WAVE_FMT_PCM: (skip) */ #define IPATCH_RIFF_WAVE_FMT_PCM 0x1 /** * IPATCH_RIFF_WAVE_FMT_FLOAT: (skip) */ #define IPATCH_RIFF_WAVE_FMT_FLOAT 0x3 GType ipatch_riff_get_type(void); GQuark ipatch_riff_error_quark(void); IpatchRiff *ipatch_riff_new(IpatchFileHandle *handle); void ipatch_riff_set_file_handle(IpatchRiff *riff, IpatchFileHandle *handle); IpatchFileHandle *ipatch_riff_get_file_handle(IpatchRiff *riff); int ipatch_riff_get_chunk_level(IpatchRiff *riff); IpatchRiffChunk *ipatch_riff_get_chunk_array(IpatchRiff *riff, int *count); IpatchRiffChunk *ipatch_riff_get_chunk(IpatchRiff *riff, int level); guint32 ipatch_riff_get_total_size(IpatchRiff *riff); guint32 ipatch_riff_get_position(IpatchRiff *riff); void ipatch_riff_push_state(IpatchRiff *riff); gboolean ipatch_riff_pop_state(IpatchRiff *riff, GError **err); IpatchRiffChunk *ipatch_riff_start_read(IpatchRiff *riff, GError **err); IpatchRiffChunk *ipatch_riff_start_read_chunk(IpatchRiff *riff, GError **err); IpatchRiffChunk *ipatch_riff_read_chunk_verify(IpatchRiff *riff, IpatchRiffChunkType type, guint32 id, GError **err); IpatchRiffChunk *ipatch_riff_read_chunk(IpatchRiff *riff, GError **err); #define ipatch_riff_write_list_chunk(parser, id, err) \ ipatch_riff_write_chunk (parser, IPATCH_RIFF_CHUNK_LIST, id, err) #define ipatch_riff_write_sub_chunk(parser, id, err) \ ipatch_riff_write_chunk (parser, IPATCH_RIFF_CHUNK_SUB, id, err) gboolean ipatch_riff_write_chunk(IpatchRiff *riff, IpatchRiffChunkType type, guint32 id, GError **err); #define ipatch_riff_end_chunk(parser, err) \ ipatch_riff_close_chunk (parser, -1, err) gboolean ipatch_riff_close_chunk(IpatchRiff *riff, int level, GError **err); #define ipatch_riff_skip_chunk(parser, err) \ ipatch_riff_skip_chunks (parser, 1, err) gboolean ipatch_riff_skip_chunks(IpatchRiff *riff, guint count, GError **err); gboolean ipatch_riff_get_error(IpatchRiff *riff, GError **err); char *ipatch_riff_message_detail(IpatchRiff *riff, int level, const char *format, ...); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2.c000066400000000000000000001205611400263525300205630ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2 * @short_description: SoundFont instrument file object * @see_also: #IpatchSF2Preset, #IpatchSF2Inst, #IpatchSF2Sample * @stability: Stable * * SoundFont version 2 instrument file object. Parent to #IpatchSF2Preset, * #IpatchSF2Inst and #IpatchSF2Sample objects. */ #include #include #include #include #include #include "IpatchParamProp.h" #include "IpatchSF2.h" #include "IpatchSF2File.h" #include "IpatchSF2Preset.h" #include "IpatchSF2Zone.h" #include "IpatchTypeProp.h" #include "IpatchVirtualContainer_types.h" #include "ipatch_priv.h" #include "version.h" /* the Info properties below shouldn't conflict, since they are composed of their 4 byte RIFF chunk ID */ enum { PROP_0, PROP_SAMPLES_24BIT /* indicates that samples should be saved in 24 bit */ }; /* !! Keep in order with IpatchSF2InfoType in IpatchSF2.h */ static guint info_ids[] = { IPATCH_SF2_VERSION, IPATCH_SF2_ENGINE, IPATCH_SF2_NAME, IPATCH_SF2_ROM_NAME, IPATCH_SF2_ROM_VERSION, IPATCH_SF2_DATE, IPATCH_SF2_AUTHOR, IPATCH_SF2_PRODUCT, IPATCH_SF2_COPYRIGHT, IPATCH_SF2_COMMENT, IPATCH_SF2_SOFTWARE }; /* parameter specs for each info property */ static GParamSpec *info_prop_pspecs[IPATCH_SF2_INFO_COUNT]; #define ERR_MSG_INVALID_INFO_ID_1 "Invalid SoundFont info ID (%d)" static void ipatch_sf2_class_init(IpatchSF2Class *klass); static void ipatch_sf2_init(IpatchSF2 *sfont); static void ipatch_sf2_finalize(GObject *gobject); static void ipatch_sf2_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sf2_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sf2_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static void ipatch_sf2_info_hash_foreach(gpointer key, gpointer value, gpointer user_data); static const GType *ipatch_sf2_container_child_types(void); static const GType *ipatch_sf2_container_virtual_types(void); static gboolean ipatch_sf2_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type); static void ipatch_sf2_container_make_unique(IpatchContainer *container, IpatchItem *item); static void ipatch_sf2_base_find_unused_locale(IpatchBase *base, int *bank, int *program, const IpatchItem *exclude, gboolean percussion); static int locale_gcompare_func(gconstpointer a, gconstpointer b); static IpatchItem * ipatch_sf2_base_find_item_by_locale(IpatchBase *base, int bank, int program); static void ipatch_sf2_real_set_info(IpatchSF2 *sf, IpatchSF2InfoType id, const char *val); static void ipatch_sf2_foreach_info_GHFunc(gpointer key, gpointer value, gpointer data); static int ipatch_sf2_info_array_qsort(const void *a, const void *b); static gpointer parent_class = NULL; static GType sf2_child_types[4] = { 0 }; static GType sf2_virt_types[6] = { 0 }; /* SoundFont item type creation function */ GType ipatch_sf2_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchSF2Class), NULL, NULL, (GClassInitFunc)ipatch_sf2_class_init, NULL, NULL, sizeof(IpatchSF2), 0, (GInstanceInitFunc)ipatch_sf2_init, }; item_type = g_type_register_static(IPATCH_TYPE_BASE, "IpatchSF2", &item_info, 0); } return (item_type); } static void ipatch_sf2_class_init(IpatchSF2Class *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass); IpatchBaseClass *base_class = IPATCH_BASE_CLASS(klass); GParamSpec **sp = &info_prop_pspecs[0]; parent_class = g_type_class_peek_parent(klass); obj_class->finalize = ipatch_sf2_finalize; obj_class->get_property = ipatch_sf2_get_property; /* we use the IpatchItem item_set_property method */ item_class->item_set_property = ipatch_sf2_set_property; item_class->copy = ipatch_sf2_item_copy; container_class->child_types = ipatch_sf2_container_child_types; container_class->virtual_types = ipatch_sf2_container_virtual_types; container_class->init_iter = ipatch_sf2_container_init_iter; container_class->make_unique = ipatch_sf2_container_make_unique; base_class->find_unused_locale = ipatch_sf2_base_find_unused_locale; base_class->find_item_by_locale = ipatch_sf2_base_find_item_by_locale; g_object_class_install_property(obj_class, PROP_SAMPLES_24BIT, g_param_spec_boolean("samples-24bit", _("Samples 24bit"), _("Enable 24 bit samples"), FALSE, G_PARAM_READWRITE)); g_object_class_override_property(obj_class, IPATCH_SF2_NAME, "title"); *sp = g_param_spec_string("version", _("Version"), _("SoundFont version (\"major.minor\")"), "2.01", G_PARAM_READWRITE); g_object_class_install_property(obj_class, IPATCH_SF2_VERSION, *sp++); *sp = ipatch_param_set(g_param_spec_string("engine", _("Engine"), _("Sound synthesis engine identifier"), "EMU8000", G_PARAM_READWRITE), "string-max-length", 255, NULL); g_object_class_install_property(obj_class, IPATCH_SF2_ENGINE, *sp++); *sp = ipatch_param_set(g_param_spec_string("name", _("Name"), _("SoundFont name"), NULL, G_PARAM_READWRITE), "string-max-length", 255, NULL); g_object_class_install_property(obj_class, IPATCH_SF2_NAME, *sp++); *sp = ipatch_param_set(g_param_spec_string("rom-name", _("ROM name"), _("ROM name identifier"), NULL, G_PARAM_READWRITE), "string-max-length", 255, NULL); g_object_class_install_property(obj_class, IPATCH_SF2_ROM_NAME, *sp++); *sp = ipatch_param_set(g_param_spec_string("rom-version", _("ROM version"), _("ROM version \"major.minor\""), NULL, G_PARAM_READWRITE), "string-max-length", 255, NULL); g_object_class_install_property(obj_class, IPATCH_SF2_ROM_VERSION, *sp++); *sp = ipatch_param_set(g_param_spec_string("date", _("Date"), _("Creation date"), NULL, G_PARAM_READWRITE), "string-max-length", 255, NULL); g_object_class_install_property(obj_class, IPATCH_SF2_DATE, *sp++); *sp = ipatch_param_set(g_param_spec_string("author", _("Author"), _("Author of SoundFont"), NULL, G_PARAM_READWRITE), "string-max-length", 255, NULL); g_object_class_install_property(obj_class, IPATCH_SF2_AUTHOR, *sp++); *sp = ipatch_param_set(g_param_spec_string("product", _("Product"), _("Product SoundFont is intended for"), NULL, G_PARAM_READWRITE), "string-max-length", 255, NULL); g_object_class_install_property(obj_class, IPATCH_SF2_PRODUCT, *sp++); *sp = ipatch_param_set(g_param_spec_string("copyright", _("Copyright"), _("Copyright"), NULL, G_PARAM_READWRITE), "string-max-length", 255, NULL); g_object_class_install_property(obj_class, IPATCH_SF2_COPYRIGHT, *sp++); *sp = ipatch_param_set(g_param_spec_string("comment", _("Comments"), _("Comments"), NULL, G_PARAM_READWRITE), "string-max-length", 65535, NULL); g_object_class_install_property(obj_class, IPATCH_SF2_COMMENT, *sp++); *sp = ipatch_param_set(g_param_spec_string("software", _("Software"), _("Software 'created by:modified by'"), NULL, G_PARAM_READWRITE), "string-max-length", 255, NULL); g_object_class_install_property(obj_class, IPATCH_SF2_SOFTWARE, *sp); sf2_child_types[0] = IPATCH_TYPE_SF2_PRESET; sf2_child_types[1] = IPATCH_TYPE_SF2_INST; sf2_child_types[2] = IPATCH_TYPE_SF2_SAMPLE; sf2_virt_types[0] = IPATCH_TYPE_VIRTUAL_SF2_MELODIC; sf2_virt_types[1] = IPATCH_TYPE_VIRTUAL_SF2_PERCUSSION; sf2_virt_types[2] = IPATCH_TYPE_VIRTUAL_SF2_INST; sf2_virt_types[3] = IPATCH_TYPE_VIRTUAL_SF2_SAMPLES; sf2_virt_types[4] = IPATCH_TYPE_VIRTUAL_SF2_ROM; } static void ipatch_sf2_init(IpatchSF2 *sfont) { sfont->ver_major = 2; sfont->ver_minor = 1; /* we convert 4 char info IDs to INTs, also set up hash table to free allocated string values */ sfont->info = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)g_free); /* set required SoundFont info to default values */ ipatch_sf2_set_info(sfont, IPATCH_SF2_NAME, _(IPATCH_BASE_DEFAULT_NAME)); ipatch_sf2_set_info(sfont, IPATCH_SF2_ENGINE, IPATCH_SF2_DEFAULT_ENGINE); ipatch_sf2_set_info(sfont, IPATCH_SF2_SOFTWARE, "libInstPatch v" IPATCH_VERSION ":"); ipatch_item_clear_flags(IPATCH_ITEM(sfont), IPATCH_BASE_CHANGED); } /* function called when SoundFont is being destroyed */ static void ipatch_sf2_finalize(GObject *gobject) { IpatchSF2 *sf = IPATCH_SF2(gobject); IPATCH_ITEM_WLOCK(sf); g_hash_table_destroy(sf->info); /* destroy SoundFont info */ sf->info = NULL; IPATCH_ITEM_WUNLOCK(sf); if(G_OBJECT_CLASS(parent_class)->finalize) { G_OBJECT_CLASS(parent_class)->finalize(gobject); } } static void ipatch_sf2_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSF2 *sf; gboolean b; g_return_if_fail(IPATCH_IS_SF2(object)); sf = IPATCH_SF2(object); if(property_id == PROP_SAMPLES_24BIT) { b = g_value_get_boolean(value); if(b) { ipatch_item_set_flags(IPATCH_ITEM(sf), IPATCH_SF2_SAMPLES_24BIT); } else { ipatch_item_clear_flags(IPATCH_ITEM(sf), IPATCH_SF2_SAMPLES_24BIT); } } else if(property_id == IPATCH_SF2_VERSION || property_id == IPATCH_SF2_ROM_VERSION) { guint major, minor; if(sscanf(g_value_get_string(value), "%u.%u", &major, &minor) != 2) { g_critical("SoundFont version property parse error"); return; } if(property_id == IPATCH_SF2_VERSION) { IPATCH_ITEM_WLOCK(sf); sf->ver_major = major; sf->ver_minor = minor; IPATCH_ITEM_WUNLOCK(sf); } else { IPATCH_ITEM_WLOCK(sf); sf->romver_major = major; sf->romver_minor = minor; IPATCH_ITEM_WUNLOCK(sf); } } else if(ipatch_sf2_info_id_is_valid(property_id)) { ipatch_sf2_real_set_info(sf, property_id, g_value_get_string(value)); /* need to do a title property notify? */ if(property_id == IPATCH_SF2_NAME) ipatch_item_prop_notify((IpatchItem *)sf, ipatch_item_pspec_title, value, NULL); } else { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ipatch_sf2_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSF2 *sf; char *s; g_return_if_fail(IPATCH_IS_SF2(object)); sf = IPATCH_SF2(object); if(property_id == PROP_SAMPLES_24BIT) { g_value_set_boolean(value, (ipatch_item_get_flags(IPATCH_ITEM(sf)) & IPATCH_SF2_SAMPLES_24BIT) != 0); } else if(property_id == IPATCH_SF2_VERSION || property_id == IPATCH_SF2_ROM_VERSION) { int major, minor; IPATCH_ITEM_RLOCK(sf); if(property_id == IPATCH_SF2_VERSION) { major = sf->ver_major; minor = sf->ver_minor; } else { major = sf->romver_major; minor = sf->romver_minor; } IPATCH_ITEM_RUNLOCK(sf); s = g_strdup_printf("%d.%d", major, minor); g_value_take_string(value, s); } else if(ipatch_sf2_info_id_is_valid(property_id)) { g_value_take_string(value, ipatch_sf2_get_info(sf, property_id)); } else { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } /* item copy function, note that this is an #IpatchBase derived object, so link_func is not used */ static void ipatch_sf2_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchSF2 *src_sf, *dest_sf; IpatchItem *newitem; GHashTable *repl_samples, *repl_insts; gboolean has_linked = FALSE; GSList *p; src_sf = IPATCH_SF2(src); dest_sf = IPATCH_SF2(dest); /* create item replacement hash */ repl_samples = g_hash_table_new(NULL, NULL); repl_insts = g_hash_table_new(NULL, NULL); IPATCH_ITEM_RLOCK(src_sf); if(ipatch_item_get_flags(src) & IPATCH_SF2_SAMPLES_24BIT) { ipatch_item_set_flags(dest, IPATCH_SF2_SAMPLES_24BIT); } else { ipatch_item_clear_flags(dest, IPATCH_SF2_SAMPLES_24BIT); } dest_sf->ver_major = src_sf->ver_major; dest_sf->ver_minor = src_sf->ver_minor; dest_sf->romver_major = src_sf->romver_major; dest_sf->romver_minor = src_sf->romver_minor; if(IPATCH_BASE(src_sf)->file) { ipatch_base_set_file(IPATCH_BASE(dest_sf), IPATCH_BASE(src_sf)->file); } /* duplicate the info variables */ g_hash_table_foreach(src_sf->info, ipatch_sf2_info_hash_foreach, dest_sf); p = src_sf->samples; while(p) /* duplicate samples */ { /* ++ ref new duplicate sample, !! sample list takes it over */ newitem = ipatch_item_duplicate((IpatchItem *)(p->data)); dest_sf->samples = g_slist_prepend(dest_sf->samples, newitem); ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_sf)); /* add to sample pointer replacement hash */ g_hash_table_insert(repl_samples, p->data, newitem); /* check if sample is stereo linked */ if(ipatch_sf2_sample_peek_linked((IpatchSF2Sample *)newitem)) { has_linked = TRUE; } p = g_slist_next(p); } /* if any linked samples exist, we must replace old linked pointers, duplicate "replace" hash wont work since samples reference items in the same list */ if(has_linked) { IpatchSF2Sample *linked; p = dest_sf->samples; while(p) { IpatchSF2Sample *sample = (IpatchSF2Sample *)(p->data); linked = ipatch_sf2_sample_peek_linked(sample); if(linked) { linked = g_hash_table_lookup(repl_samples, linked); ipatch_sf2_sample_set_linked(sample, linked); } p = g_slist_next(p); } } p = src_sf->insts; while(p) /* duplicate instruments */ { /* ++ ref new duplicate instrument, !! instrument list takes it over * duplicate instrument and replace referenced sample pointers. */ newitem = ipatch_item_duplicate_replace((IpatchItem *)(p->data), repl_samples); dest_sf->insts = g_slist_prepend(dest_sf->insts, newitem); ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_sf)); /* add to instrument pointer replacement hash */ g_hash_table_insert(repl_insts, p->data, newitem); p = g_slist_next(p); } p = src_sf->presets; while(p) /* duplicate presets */ { /* ++ ref new duplicate preset, !! preset list takes it over * duplicate preset and replace referenced instrument pointers. */ newitem = ipatch_item_duplicate_replace((IpatchItem *)(p->data), repl_insts); dest_sf->presets = g_slist_prepend(dest_sf->presets, newitem); ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_sf)); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(src_sf); dest_sf->presets = g_slist_reverse(dest_sf->presets); dest_sf->insts = g_slist_reverse(dest_sf->insts); dest_sf->samples = g_slist_reverse(dest_sf->samples); g_hash_table_destroy(repl_samples); g_hash_table_destroy(repl_insts); } static void ipatch_sf2_info_hash_foreach(gpointer key, gpointer value, gpointer user_data) { char *val = (char *)value; IpatchSF2 *dup = (IpatchSF2 *)user_data; ipatch_sf2_set_info(dup, GPOINTER_TO_UINT(key), val); } static const GType * ipatch_sf2_container_child_types(void) { return (sf2_child_types); } static const GType * ipatch_sf2_container_virtual_types(void) { return (sf2_virt_types); } /* container is locked by caller */ static gboolean ipatch_sf2_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type) { IpatchSF2 *sfont = IPATCH_SF2(container); if(g_type_is_a(type, IPATCH_TYPE_SF2_PRESET)) { ipatch_iter_GSList_init(iter, &sfont->presets); } else if(g_type_is_a(type, IPATCH_TYPE_SF2_INST)) { ipatch_iter_GSList_init(iter, &sfont->insts); } else if(g_type_is_a(type, IPATCH_TYPE_SF2_SAMPLE)) { ipatch_iter_GSList_init(iter, &sfont->samples); } else { g_critical("Invalid child type '%s' for parent of type '%s'", g_type_name(type), g_type_name(G_OBJECT_TYPE(container))); return (FALSE); } return (TRUE); } static void ipatch_sf2_container_make_unique(IpatchContainer *container, IpatchItem *item) { IpatchSF2 *sfont = IPATCH_SF2(container); char *name, *newname; IPATCH_ITEM_WLOCK(sfont); if(IPATCH_IS_SF2_PRESET(item)) { int bank, newbank, program, newprogram; ipatch_sf2_preset_get_midi_locale(IPATCH_SF2_PRESET(item), &bank, &program); newbank = bank; newprogram = program; ipatch_base_find_unused_midi_locale(IPATCH_BASE(sfont), &newbank, &newprogram, item, newbank == 128); if(bank != newbank || program != newprogram) ipatch_sf2_preset_set_midi_locale(IPATCH_SF2_PRESET(item), newbank, newprogram); } else if(!IPATCH_IS_SF2_INST(item) && !IPATCH_IS_SF2_SAMPLE(item)) { g_critical("Invalid child type '%s' for IpatchSF2 object", g_type_name(G_TYPE_FROM_INSTANCE(item))); return; } g_object_get(item, "name", &name, NULL); newname = ipatch_sf2_make_unique_name(sfont, G_TYPE_FROM_INSTANCE(item), name, NULL); if(!name || strcmp(name, newname) != 0) { g_object_set(item, "name", newname, NULL); } IPATCH_ITEM_WUNLOCK(sfont); g_free(name); g_free(newname); } /* base method to find an unused MIDI bank:program locale */ static void ipatch_sf2_base_find_unused_locale(IpatchBase *base, int *bank, int *program, const IpatchItem *exclude, gboolean percussion) { IpatchSF2 *sf = IPATCH_SF2(base); GSList *locale_list = NULL; IpatchSF2Preset *pset; GSList *p; guint b, n; /* Stores current bank and program number */ guint lbank, lprogram; if(percussion) { *bank = 128; } /* fill array with bank and program numbers */ IPATCH_ITEM_RLOCK(sf); p = sf->presets; while(p) { pset = (IpatchSF2Preset *)(p->data); /* only add to locale list if not the exclude item */ if((gpointer)pset != (gpointer)exclude) locale_list = g_slist_prepend(locale_list, GUINT_TO_POINTER (((guint32)pset->bank << 16) | pset->program)); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(sf); if(!locale_list) { return; } locale_list = g_slist_sort(locale_list, (GCompareFunc)locale_gcompare_func); b = *bank; n = *program; /* loop through sorted list of bank:programs */ p = locale_list; while(p) { lprogram = GPOINTER_TO_UINT(p->data); lbank = lprogram >> 16; lprogram &= 0xFFFF; if(lbank > b || (lbank == b && lprogram > n)) { break; } if(lbank >= b) { if(++n > 127) { n = 0; b++; } } p = g_slist_delete_link(p, p); /* delete and advance */ } *bank = b; *program = n; if(p) { g_slist_free(p); /* free remainder of list */ } } /* function used to do a temporary sort on preset list for ipatch_sf2_base_find_unused_locale */ static int locale_gcompare_func(gconstpointer a, gconstpointer b) { return (GPOINTER_TO_UINT(a) - GPOINTER_TO_UINT(b)); } static IpatchItem * ipatch_sf2_base_find_item_by_locale(IpatchBase *base, int bank, int program) { IpatchSF2Preset *preset; preset = ipatch_sf2_find_preset(IPATCH_SF2(base), NULL, bank, program, NULL); return ((IpatchItem *)preset); } /** * ipatch_sf2_new: * * Create a new SoundFont base object. * * Returns: New SoundFont base object with a reference count of 1. Caller * owns the reference and removing it will destroy the item. */ IpatchSF2 * ipatch_sf2_new(void) { return (IPATCH_SF2(g_object_new(IPATCH_TYPE_SF2, NULL))); } /** * ipatch_sf2_set_file: * @sf: SoundFont to set file object of * @file: File object to use with the SoundFont. * * Sets the file object of a SoundFont. SoundFont files are kept open * for sample data that references the file. This function sets a * SoundFonts authoritive file object. A convenience function, as * ipatch_base_set_file() does the same thing (albeit without more specific * type casting). */ void ipatch_sf2_set_file(IpatchSF2 *sf, IpatchSF2File *file) { g_return_if_fail(IPATCH_IS_SF2(sf)); g_return_if_fail(IPATCH_IS_SF2_FILE(file)); ipatch_base_set_file(IPATCH_BASE(sf), IPATCH_FILE(file)); } /** * ipatch_sf2_get_file: * @sf: SoundFont to get file object of * * Gets the file object of a SoundFont. The returned SoundFont file object's * reference count has incremented. The caller owns the reference and is * responsible for removing it with g_object_unref(). * A convenience function as ipatch_base_get_file() does the same thing * (albeit without more specific type casting). * * Returns: (transfer full): The SoundFont file object or %NULL if @sf is not open. Remember * to unref the file object with g_object_unref() when * done with it. */ IpatchSF2File * ipatch_sf2_get_file(IpatchSF2 *sf) { IpatchFile *file; g_return_val_if_fail(IPATCH_IS_SF2(sf), NULL); file = ipatch_base_get_file(IPATCH_BASE(sf)); if(file) { return (IPATCH_SF2_FILE(file)); } else { return (NULL); } } /** * ipatch_sf2_get_info: * @sf: SoundFont to get info from * @id: RIFF FOURCC id * * Get a SoundFont info string by RIFF FOURCC ID. * * Returns: New allocated info string value or %NULL if no info with the * given @id. String should be freed when finished with it. */ char * ipatch_sf2_get_info(IpatchSF2 *sf, IpatchSF2InfoType id) { char *val; g_return_val_if_fail(IPATCH_IS_SF2(sf), NULL); IPATCH_ITEM_RLOCK(sf); val = g_hash_table_lookup(sf->info, GUINT_TO_POINTER(id)); if(val) { val = g_strdup(val); } IPATCH_ITEM_RUNLOCK(sf); return (val); } /** * ipatch_sf2_set_info: * @sf: SoundFont to set info of * @id: RIFF FOURCC id * @val: Value to set info to or %NULL to unset (clear) info. * * Set SoundFont info. Validates @id and ensures @val does not exceed * the maximum allowed length for the given info type. * * Emits changed signal on SoundFont. */ void ipatch_sf2_set_info(IpatchSF2 *sf, IpatchSF2InfoType id, const char *val) { GParamSpec *pspec; GValue oldval = { 0 }, newval = { 0 }; int i; g_return_if_fail(IPATCH_IS_SF2(sf)); for(i = 0; i < G_N_ELEMENTS(info_ids); i++) if(info_ids[i] == id) { break; } if(i == G_N_ELEMENTS(info_ids)) { g_return_if_fail(ipatch_sf2_info_id_is_valid(id)); /* for debugging */ return; } pspec = info_prop_pspecs[i]; g_value_init(&oldval, G_TYPE_STRING); g_value_take_string(&oldval, ipatch_sf2_get_info(sf, id)); ipatch_sf2_real_set_info(sf, id, val); g_value_init(&newval, G_TYPE_STRING); g_value_set_static_string(&newval, val); ipatch_item_prop_notify((IpatchItem *)sf, pspec, &newval, &oldval); /* need to do a title property notify? */ if(id == IPATCH_SF2_NAME) ipatch_item_prop_notify((IpatchItem *)sf, ipatch_item_pspec_title, &newval, &oldval); g_value_unset(&oldval); g_value_unset(&newval); } /* the real set info by id routine, user routine does a property notify */ static void ipatch_sf2_real_set_info(IpatchSF2 *sf, IpatchSF2InfoType id, const char *val) { char *newval = NULL; guint maxlen; maxlen = ipatch_sf2_get_info_max_size(id); /* value exceeds max length? */ if(maxlen > 0 && val && strlen(val) > maxlen - 1) { g_warning("IpatchSF2Info string with id '%.4s' truncated", (char *)&id); newval = g_strndup(val, maxlen - 1); } else if(val) { newval = g_strdup(val); } /* we set up the hash table to free old string values */ IPATCH_ITEM_WLOCK(sf); if(newval) { g_hash_table_insert(sf->info, GINT_TO_POINTER(id), newval); } else { g_hash_table_remove(sf->info, GINT_TO_POINTER(id)); } IPATCH_ITEM_WUNLOCK(sf); /* newval has been taken over by hash table */ } /* a bag for ipatch_sf2_get_info_array() */ typedef struct { int count; IpatchSF2Info *info; } InfoArrayBag; /** * ipatch_sf2_get_info_array: (skip) * @sf: SoundFont to get all info strings from * * Get all string info (not IPATCH_SF2_VERSION or IPATCH_SF2_ROM_VERSION) * from a SoundFont object. The array is sorted in the order recommended * by the SoundFont standard for saving. * * Returns: A newly allocated array of info structures terminated by * an array member with 0 valued id * field. Remember to free the array with ipatch_sf2_free_info_array() * when finished with it. */ IpatchSF2Info * ipatch_sf2_get_info_array(IpatchSF2 *sf) { IpatchSF2Info *array; InfoArrayBag bag; g_return_val_if_fail(IPATCH_IS_SF2(sf), NULL); /* allocate max expected info elements + 1 for terminator */ array = g_malloc((IPATCH_SF2_INFO_COUNT + 1) * sizeof(IpatchSF2Info)); bag.count = 0; bag.info = array; IPATCH_ITEM_RLOCK(sf); g_hash_table_foreach(sf->info, ipatch_sf2_foreach_info_GHFunc, &bag); IPATCH_ITEM_RUNLOCK(sf); qsort(array, bag.count, sizeof(IpatchSF2Info), ipatch_sf2_info_array_qsort); /* terminate array */ array[bag.count].id = 0; array[bag.count].val = NULL; /* re-allocate for actual size */ return (g_realloc(array, (bag.count + 1) * sizeof(IpatchSF2Info))); } static void ipatch_sf2_foreach_info_GHFunc(gpointer key, gpointer value, gpointer data) { InfoArrayBag *bag = (InfoArrayBag *)data; /* shouldn't happen, but just in case */ if(bag->count >= IPATCH_SF2_INFO_COUNT) { return; } /* store the info ID and string in the info array */ bag->info[bag->count].id = GPOINTER_TO_UINT(key); bag->info[bag->count].val = g_strdup((char *)value); bag->count++; /* advance to next index */ } /* sorts an info array according to recommended SoundFont order */ static int ipatch_sf2_info_array_qsort(const void *a, const void *b) { IpatchSF2Info *ainfo = (IpatchSF2Info *)a; IpatchSF2Info *binfo = (IpatchSF2Info *)b; int andx, bndx; /* find index in info ID array */ for(andx = 0; andx < G_N_ELEMENTS(info_ids); andx++) if(info_ids[andx] == ainfo->id) { break; } for(bndx = 0; bndx < G_N_ELEMENTS(info_ids); bndx++) if(info_ids[bndx] == binfo->id) { break; } return (andx - bndx); } /** * ipatch_sf2_free_info_array: (skip) * @array: SoundFont info array * * Frees an info array returned by ipatch_sf2_get_info_array(). */ void ipatch_sf2_free_info_array(IpatchSF2Info *array) { int i; g_return_if_fail(array != NULL); for(i = 0; array[i].id; i++) { g_free(array[i].val); } g_free(array); } /** * ipatch_sf2_info_id_is_valid: (skip) * @id: RIFF FOURCC id (see #IpatchSF2InfoType) * * Check if a given RIFF FOURCC id is a valid SoundFont info id. * * Returns: %TRUE if @id is a valid info id, %FALSE otherwise */ gboolean ipatch_sf2_info_id_is_valid(guint32 id) { int i; for(i = 0; i < G_N_ELEMENTS(info_ids) ; i++) if(info_ids[i] == id) { return (TRUE); } return (FALSE); } /** * ipatch_sf2_get_info_max_size: (skip) * @infotype: An info enumeration * * Get maximum chunk size for info chunks. * * NOTE: Max size includes terminating NULL character so subtract one from * returned value to get max allowed string length. * * Returns: Maximum info chunk size or 0 if invalid @infotype. Subtract one * to get max allowed string length (if returned value was not 0). */ int ipatch_sf2_get_info_max_size(IpatchSF2InfoType infotype) { if(!ipatch_sf2_info_id_is_valid(infotype)) { return (0); } if(infotype == IPATCH_SF2_COMMENT) /* comments can have up to 64k bytes */ { return (65536); } if(infotype == IPATCH_SF2_VERSION /* versions take up 4 bytes */ || infotype == IPATCH_SF2_ROM_VERSION) { return (4); } return (256); /* all other valid info types allow 256 bytes max */ } /** * ipatch_sf2_find_preset: * @sf: SoundFont to search in * @name: (nullable): Name of preset to find or %NULL to match any name * @bank: MIDI bank number of preset to search for or -1 to not search by * MIDI bank:program numbers * @program: MIDI program number of preset to search for, only used * if @bank is 0-128 * @exclude: (nullable): A preset to exclude from the search or %NULL * * Find a preset by name or bank:preset MIDI numbers. If preset @name * and @bank:@program are specified then match for either condition. * If a preset is found its reference count is incremented before it * is returned. The caller is responsible for removing the reference * with g_object_unref() when finished with it. * * Returns: (transfer full): The matching preset or %NULL if not found. Remember to unref * the item when finished with it. */ IpatchSF2Preset * ipatch_sf2_find_preset(IpatchSF2 *sf, const char *name, int bank, int program, const IpatchSF2Preset *exclude) { IpatchSF2Preset *pset; gboolean bynum = FALSE; GSList *p; g_return_val_if_fail(IPATCH_IS_SF2(sf), NULL); /* if bank and program are valid, then search by number */ if(bank >= 0 && bank <= 128 && program >= 0 && program < 128) { bynum = TRUE; } IPATCH_ITEM_RLOCK(sf); p = sf->presets; while(p) { pset = (IpatchSF2Preset *)(p->data); IPATCH_ITEM_RLOCK(pset); /* MT - Recursive LOCK */ if(pset != exclude /* if exclude is NULL it will never == pset */ && ((bynum && pset->bank == bank && pset->program == program) || (name && strcmp(pset->name, name) == 0))) { g_object_ref(pset); IPATCH_ITEM_RUNLOCK(pset); IPATCH_ITEM_RUNLOCK(sf); return (pset); } IPATCH_ITEM_RUNLOCK(pset); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(sf); return (NULL); } /** * ipatch_sf2_find_inst: * @sf: SoundFont to search in * @name: Name of Instrument to find * @exclude: (nullable): An instrument to exclude from the search or %NULL * * Find an instrument by @name in a SoundFont. If a matching instrument * is found, its reference count is incremented before it is returned. * The caller is responsible for removing the reference with g_object_unref() * when finished with it. * * Returns: (transfer full): The matching instrument or %NULL if not found. Remember to unref * the item when finished with it. */ IpatchSF2Inst * ipatch_sf2_find_inst(IpatchSF2 *sf, const char *name, const IpatchSF2Inst *exclude) { IpatchSF2Inst *inst; GSList *p; g_return_val_if_fail(IPATCH_IS_SF2(sf), NULL); g_return_val_if_fail(name != NULL, NULL); IPATCH_ITEM_RLOCK(sf); p = sf->insts; while(p) { inst = (IpatchSF2Inst *)(p->data); IPATCH_ITEM_RLOCK(inst); /* MT - Recursive LOCK */ if(inst != exclude && strcmp(inst->name, name) == 0) { g_object_ref(inst); IPATCH_ITEM_RUNLOCK(inst); IPATCH_ITEM_RUNLOCK(sf); return (inst); } IPATCH_ITEM_RUNLOCK(inst); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(sf); return (NULL); } /** * ipatch_sf2_find_sample: * @sf: SoundFont to search in * @name: Name of sample to find * @exclude: (nullable): A sample to exclude from the search or %NULL * * Find a sample by @name in a SoundFont. If a sample is found its * reference count is incremented before it is returned. The caller * is responsible for removing the reference with g_object_unref() * when finished with it. * * Returns: (transfer full): The matching sample or %NULL if not found. Remember to unref * the item when finished with it. */ IpatchSF2Sample * ipatch_sf2_find_sample(IpatchSF2 *sf, const char *name, const IpatchSF2Sample *exclude) { IpatchSF2Sample *sample; GSList *p; g_return_val_if_fail(IPATCH_IS_SF2(sf), NULL); g_return_val_if_fail(name != NULL, NULL); IPATCH_ITEM_RLOCK(sf); p = sf->samples; while(p) { sample = (IpatchSF2Sample *)(p->data); IPATCH_ITEM_RLOCK(sample); /* MT - Recursive LOCK */ if(sample != exclude && strcmp(sample->name, name) == 0) { g_object_ref(sample); IPATCH_ITEM_RUNLOCK(sample); IPATCH_ITEM_RUNLOCK(sf); return (sample); } IPATCH_ITEM_RUNLOCK(sample); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(sf); return (NULL); } /** * ipatch_sf2_get_zone_references: * @item: Item to locate referencing zones of, must be of type #IpatchSF2Inst * or #IpatchSF2Sample and be parented to an #IpatchSF2 object. * * Get list of zones referencing an IpatchSF2Inst or IpatchSF2Sample. * * Returns: (transfer full): New object list containing #IpatchSF2Zone objects that * refer to @item. The new list object has a reference count of 1 * which the caller owns, unreference to free the list. */ IpatchList * ipatch_sf2_get_zone_references(IpatchItem *item) { IpatchList *reflist, *itemlist, *zonelist; IpatchSF2 *sf; IpatchSF2Zone *zone; IpatchIter iter, zone_iter; IpatchItem *pitem; g_return_val_if_fail(IPATCH_IS_SF2_INST(item) || IPATCH_IS_SF2_SAMPLE(item), NULL); pitem = ipatch_item_get_parent(item); g_return_val_if_fail(IPATCH_IS_SF2(pitem), NULL); sf = IPATCH_SF2(pitem); /* ++ ref item list */ if(IPATCH_IS_SF2_INST(item)) /* is an instrument? */ { itemlist = ipatch_sf2_get_presets(sf); } else { itemlist = ipatch_sf2_get_insts(sf); /* its a sample */ } reflist = ipatch_list_new(); /* ++ ref new list */ ipatch_list_init_iter(itemlist, &iter); pitem = ipatch_item_first(&iter); while(pitem) /* loop on item list */ { /* ++ ref new zone list */ zonelist = ipatch_container_get_children((IpatchContainer *)(pitem), IPATCH_TYPE_SF2_ZONE); ipatch_list_init_iter(zonelist, &zone_iter); zone = ipatch_sf2_zone_first(&zone_iter); while(zone) { if(ipatch_sf2_zone_peek_link_item(zone) == item) { g_object_ref(zone); /* ++ ref zone for new list */ reflist->items = g_list_prepend(reflist->items, zone); } zone = ipatch_sf2_zone_next(&zone_iter); } g_object_unref(zonelist); /* -- unref zone list */ pitem = ipatch_item_next(&iter); } g_object_unref(itemlist); /* -- unref item list */ return (reflist); /* !! caller takes over reference */ } /* In theory there is still a chance of duplicates if another item's name is set to the generated unique one (by another thread) while in this routine */ /** * ipatch_sf2_make_unique_name: * @sfont: SoundFont item * @child_type: A child type of @sfont to search for a unique name in * @name: (nullable): An initial name to use or %NULL * @exclude: (nullable): An item to exclude from search or %NULL * * Generates a unique name for the given @child_type in @sfont. The @name * parameter is used as a base and is modified, by appending a number, to * make it unique (if necessary). The @exclude parameter is used to exclude * an existing @sfont child item from the search. * * MT-Note: To ensure that an item is actually unique before being * added to a SoundFont object, ipatch_container_add_unique() should be * used. * * Returns: A new unique name which should be freed when finished with it. */ char * ipatch_sf2_make_unique_name(IpatchSF2 *sfont, GType child_type, const char *name, const IpatchItem *exclude) { GSList **list, *p; char curname[IPATCH_SFONT_NAME_SIZE + 1]; int name_ofs; int count = 2; g_return_val_if_fail(IPATCH_IS_SF2(sfont), NULL); if(child_type == IPATCH_TYPE_SF2_PRESET) { list = &sfont->presets; name_ofs = G_STRUCT_OFFSET(IpatchSF2Preset, name); if(!name) { name = _("New Preset"); } } else if(child_type == IPATCH_TYPE_SF2_INST) { list = &sfont->insts; name_ofs = G_STRUCT_OFFSET(IpatchSF2Inst, name); if(!name) { name = _("New Instrument"); } } else if(child_type == IPATCH_TYPE_SF2_SAMPLE) { list = &sfont->samples; name_ofs = G_STRUCT_OFFSET(IpatchSF2Sample, name); if(!name) { name = _("New Sample"); } } else { g_critical(IPATCH_CONTAINER_ERRMSG_INVALID_CHILD_2, g_type_name(child_type), g_type_name(IPATCH_TYPE_SF2)); return (NULL); } g_strlcpy(curname, name, sizeof(curname)); IPATCH_ITEM_RLOCK(sfont); p = *list; while(p) /* check for duplicate */ { IPATCH_ITEM_RLOCK(p->data); /* MT - Recursive LOCK */ if(p->data != exclude && strcmp(G_STRUCT_MEMBER(char *, p->data, name_ofs), curname) == 0) { /* duplicate name */ IPATCH_ITEM_RUNLOCK(p->data); ipatch_strconcat_num(name, count++, curname, sizeof(curname)); p = *list; /* start over */ continue; } IPATCH_ITEM_RUNLOCK(p->data); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(sfont); return (g_strdup(curname)); } libinstpatch-1.1.6/libinstpatch/IpatchSF2.h000066400000000000000000000131701400263525300205650ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_H__ #define __IPATCH_SF2_H__ #include #include #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchSF2 IpatchSF2; typedef struct _IpatchSF2Class IpatchSF2Class; #define IPATCH_TYPE_SF2 (ipatch_sf2_get_type ()) #define IPATCH_SF2(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SF2, IpatchSF2)) #define IPATCH_SF2_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SF2, IpatchSF2Class)) #define IPATCH_IS_SF2(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SF2)) #define IPATCH_IS_SF2_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SF2)) #define IPATCH_SF2_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SF2, IpatchSF2Class)) /* SoundFont object */ struct _IpatchSF2 { /*< public >*/ IpatchBase parent_instance; guint16 ver_major, ver_minor; /* SoundFont version */ guint16 romver_major, romver_minor; /* ROM version (if any) */ GHashTable *info; /* hash of info strings by 4 char ID */ GSList *presets; /* list of #IpatchSF2Preset structures */ GSList *insts; /* list of #IpatchSF2Inst structures */ GSList *samples; /* list of #IpatchSF2Sample structures */ }; /* SoundFont class */ struct _IpatchSF2Class { IpatchBaseClass parent_class; }; /** * IpatchSF2Flags: * @IPATCH_SF2_SAMPLES_24BIT: SoundFont 24 bit samples enabled flag */ typedef enum { IPATCH_SF2_SAMPLES_24BIT = 1 << IPATCH_BASE_UNUSED_FLAG_SHIFT } IpatchSF2Flags; /** * IPATCH_SF2_UNUSED_FLAG_SHIFT: (skip) */ /* we reserve a couple flags for backwards compatible expansion */ #define IPATCH_SF2_UNUSED_FLAG_SHIFT (IPATCH_BASE_UNUSED_FLAG_SHIFT + 3) /* SoundFont INFO enums (keep order synced with IpatchSF2.c) */ typedef enum { IPATCH_SF2_UNKNOWN, /* unknown chunk ("NULL" value) */ IPATCH_SF2_VERSION = IPATCH_SFONT_FOURCC_IFIL, /* SoundFont version */ IPATCH_SF2_ENGINE = IPATCH_SFONT_FOURCC_ISNG,/* target sound engine */ IPATCH_SF2_NAME = IPATCH_SFONT_FOURCC_INAM, /* SoundFont name */ IPATCH_SF2_ROM_NAME = IPATCH_SFONT_FOURCC_IROM, /* ROM name */ IPATCH_SF2_ROM_VERSION = IPATCH_SFONT_FOURCC_IVER, /* ROM version */ IPATCH_SF2_DATE = IPATCH_SFONT_FOURCC_ICRD, /* creation date */ IPATCH_SF2_AUTHOR = IPATCH_SFONT_FOURCC_IENG,/* sound designers/engineers */ IPATCH_SF2_PRODUCT = IPATCH_SFONT_FOURCC_IPRD, /* product intended for */ IPATCH_SF2_COPYRIGHT = IPATCH_SFONT_FOURCC_ICOP, /* copyright message */ IPATCH_SF2_COMMENT = IPATCH_SFONT_FOURCC_ICMT, /* comments */ IPATCH_SF2_SOFTWARE = IPATCH_SFONT_FOURCC_ISFT /* software "create:modify" */ } IpatchSF2InfoType; /** * IPATCH_SF2_INFO_COUNT: (skip) */ #define IPATCH_SF2_INFO_COUNT 11 /** * IPATCH_SF2_DEFAULT_ENGINE: (skip) */ #define IPATCH_SF2_DEFAULT_ENGINE "EMU8000" /* structure used for ipatch_sf2_get_info_array */ typedef struct { guint32 id; /* FOURCC info id */ char *val; /* info string value */ } IpatchSF2Info; GType ipatch_sf2_get_type(void); IpatchSF2 *ipatch_sf2_new(void); #define ipatch_sf2_get_presets(sfont) \ ipatch_container_get_children (IPATCH_CONTAINER (sfont), \ IPATCH_TYPE_SF2_PRESET) #define ipatch_sf2_get_insts(sfont) \ ipatch_container_get_children (IPATCH_CONTAINER (sfont), \ IPATCH_TYPE_SF2_INST) #define ipatch_sf2_get_samples(sfont) \ ipatch_container_get_children (IPATCH_CONTAINER (sfont), \ IPATCH_TYPE_SF2_SAMPLE) void ipatch_sf2_set_file(IpatchSF2 *sf, IpatchSF2File *file); IpatchSF2File *ipatch_sf2_get_file(IpatchSF2 *sf); char *ipatch_sf2_get_info(IpatchSF2 *sf, IpatchSF2InfoType id); void ipatch_sf2_set_info(IpatchSF2 *sf, IpatchSF2InfoType id, const char *val); IpatchSF2Info *ipatch_sf2_get_info_array(IpatchSF2 *sf); void ipatch_sf2_free_info_array(IpatchSF2Info *array); gboolean ipatch_sf2_info_id_is_valid(guint32 id); int ipatch_sf2_get_info_max_size(IpatchSF2InfoType infotype); IpatchSF2Preset *ipatch_sf2_find_preset(IpatchSF2 *sf, const char *name, int bank, int program, const IpatchSF2Preset *exclude); IpatchSF2Inst *ipatch_sf2_find_inst(IpatchSF2 *sf, const char *name, const IpatchSF2Inst *exclude); IpatchSF2Sample *ipatch_sf2_find_sample(IpatchSF2 *sf, const char *name, const IpatchSF2Sample *exclude); IpatchList *ipatch_sf2_get_zone_references(IpatchItem *item); char *ipatch_sf2_make_unique_name(IpatchSF2 *sfont, GType child_type, const char *name, const IpatchItem *exclude); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2File.c000066400000000000000000000251531400263525300213640ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2File * @short_description: SoundFont file object * @see_also: * @stability: Stable * * A #IpatchFile object type specifically for SoundFont files. */ #include #include #include #include "IpatchSF2File.h" #include "IpatchSF2File_priv.h" #include "ipatch_priv.h" /* IpatchSF2File properties */ enum { PROP_FILE_0, PROP_FILE_SAMPLE_POS, /* position in file of sample chunk */ PROP_FILE_SAMPLE_SIZE, /* size of sample chunk */ PROP_FILE_SAMPLE24_POS /* position in file of LS bytes of 24 bit samples */ }; static void ipatch_sf2_file_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sf2_file_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gboolean ipatch_sf2_file_identify(IpatchFile *file, IpatchFileHandle *handle, GError **err); static gboolean ipatch_sf2_file_real_set_sample_pos(IpatchSF2File *file, guint sample_pos); static gboolean ipatch_sf2_file_real_set_sample_size(IpatchSF2File *file, guint sample_size); static gboolean ipatch_sf2_file_real_set_sample24_pos(IpatchSF2File *file, guint sample24_pos); G_DEFINE_TYPE(IpatchSF2File, ipatch_sf2_file, IPATCH_TYPE_FILE) /* SoundFont file class init function */ static void ipatch_sf2_file_class_init(IpatchSF2FileClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchFileClass *file_class = IPATCH_FILE_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->get_property = ipatch_sf2_file_get_property; file_class->identify = ipatch_sf2_file_identify; item_class->item_set_property = ipatch_sf2_file_set_property; g_object_class_install_property(obj_class, PROP_FILE_SAMPLE_POS, g_param_spec_uint("sample-pos", "Sample Chunk Position", "Position in file of sample data chunk", 0, 0xFFFFFFFF, 0, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_FILE_SAMPLE_SIZE, g_param_spec_uint("sample-size", "Sample Chunk Size", "Size of sample data chunk, in samples", 0, 0xFFFFFFFF, 0, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_FILE_SAMPLE24_POS, g_param_spec_uint("sample24-pos", "Sample24 Chunk Position", "Position in file of 24 bit sample chunk", 0, 0xFFFFFFFF, 0, G_PARAM_READWRITE)); } static void ipatch_sf2_file_init(IpatchSF2File *file) { } static void ipatch_sf2_file_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSF2File *sfont_file = IPATCH_SF2_FILE(object); switch(property_id) { case PROP_FILE_SAMPLE_POS: ipatch_sf2_file_real_set_sample_pos(sfont_file, g_value_get_uint(value)); break; case PROP_FILE_SAMPLE_SIZE: ipatch_sf2_file_real_set_sample_size(sfont_file, g_value_get_uint(value)); break; case PROP_FILE_SAMPLE24_POS: ipatch_sf2_file_real_set_sample24_pos(sfont_file, g_value_get_uint(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_sf2_file_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSF2File *sfont_file = IPATCH_SF2_FILE(object); switch(property_id) { case PROP_FILE_SAMPLE_POS: g_value_set_uint(value, ipatch_sf2_file_get_sample_pos(sfont_file)); break; case PROP_FILE_SAMPLE_SIZE: g_value_set_uint(value, ipatch_sf2_file_get_sample_size(sfont_file)); break; case PROP_FILE_SAMPLE24_POS: g_value_set_uint(value, ipatch_sf2_file_get_sample24_pos(sfont_file)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } /* SoundFont file identification method */ static gboolean ipatch_sf2_file_identify(IpatchFile *file, IpatchFileHandle *handle, GError **err) { guint32 buf[3]; char *filename; int len; if(handle) /* Test content */ { if(!ipatch_file_read(handle, buf, 12, err)) { return (FALSE); } if(buf[0] == IPATCH_FOURCC_RIFF && buf[2] == IPATCH_SFONT_FOURCC_SFBK) { return (TRUE); } } /* Test file name extension */ else if((filename = ipatch_file_get_name(file))) /* ++ alloc file name */ { len = strlen(filename); if(len >= 4 && g_ascii_strcasecmp(filename + len - 4, ".sf2") == 0) { g_free(filename); /* -- free file name */ return (TRUE); } g_free(filename); /* -- free file name */ } return (FALSE); } /** * ipatch_sf2_file_new: * * Create a new SoundFont file object. * * Returns: New SoundFont file object (derived from IpatchFile) with a * reference count of 1. Caller owns the reference and removing it will * destroy the item. */ IpatchSF2File * ipatch_sf2_file_new(void) { return (IPATCH_SF2_FILE(g_object_new(IPATCH_TYPE_SF2_FILE, NULL))); } /** * ipatch_sf2_file_set_sample_pos: * @file: SoundFont file object to set position of sample chunk * @sample_pos: Position in the SoundFont file of the first sample of the * sample data chunk, in bytes * * Sets the position of the sample data chunk in a SoundFont file object. */ void ipatch_sf2_file_set_sample_pos(IpatchSF2File *file, guint sample_pos) { if(ipatch_sf2_file_real_set_sample_pos(file, sample_pos)) { g_object_notify(G_OBJECT(file), "sample-pos"); } } /* the real SoundFont file set sample chunk position routine */ static gboolean ipatch_sf2_file_real_set_sample_pos(IpatchSF2File *file, guint sample_pos) { g_return_val_if_fail(IPATCH_IS_SF2_FILE(file), FALSE); /* atomic write */ file->sample_pos = sample_pos; return (TRUE); } /** * ipatch_sf2_file_get_sample_pos: * @file: SoundFont file object to get position of sample chunk from * * Gets the position of the sample data chunk in a SoundFont file object. * * Returns: Position in the SoundFont file of the first sample of the * sample data chunk, in bytes */ guint ipatch_sf2_file_get_sample_pos(IpatchSF2File *file) { guint pos; g_return_val_if_fail(IPATCH_IS_SF2_FILE(file), 0); /* atomic read */ pos = file->sample_pos; return (pos); } /** * ipatch_sf2_file_set_sample_size: * @file: SoundFont file object to set the size of the sample chunk * @sample_size: Size of the sample data chunk, in samples * * Sets the size of the sample data chunk in a SoundFont file object. */ void ipatch_sf2_file_set_sample_size(IpatchSF2File *file, guint sample_size) { if(ipatch_sf2_file_real_set_sample_size(file, sample_size)) { g_object_notify(G_OBJECT(file), "sample-size"); } } /* the real SoundFont file set sample chunk size routine */ static gboolean ipatch_sf2_file_real_set_sample_size(IpatchSF2File *file, guint sample_size) { g_return_val_if_fail(IPATCH_IS_SF2_FILE(file), FALSE); /* atomic write */ file->sample_size = sample_size; return (TRUE); } /** * ipatch_sf2_file_get_sample_size: * @file: SoundFont file object to get the size of the sample chunk from * * Gets the size of the sample data chunk in a SoundFont file object. * * Returns: Size of the sample data chunk, in samples */ guint ipatch_sf2_file_get_sample_size(IpatchSF2File *file) { guint size; g_return_val_if_fail(IPATCH_IS_SF2_FILE(file), 0); /* atomic read */ size = file->sample_size; return (size); } /** * ipatch_sf2_file_set_sample24_pos: * @file: SoundFont file object to set position of sample24 chunk * @sample24_pos: Position in the SoundFont file of the first sample of the * sample 24 data chunk, in bytes * * Sets the position of the sample 24 data chunk in a SoundFont file object. * This optional chunk contains the lower significant bytes of 24 bit samples. */ void ipatch_sf2_file_set_sample24_pos(IpatchSF2File *file, guint sample24_pos) { if(ipatch_sf2_file_real_set_sample24_pos(file, sample24_pos)) { g_object_notify(G_OBJECT(file), "sample24-pos"); } } /* the real SoundFont file set sample24 chunk position routine */ static gboolean ipatch_sf2_file_real_set_sample24_pos(IpatchSF2File *file, guint sample24_pos) { g_return_val_if_fail(IPATCH_IS_SF2_FILE(file), FALSE); /* atomic write */ file->sample24_pos = sample24_pos; return (TRUE); } /** * ipatch_sf2_file_get_sample24_pos: * @file: SoundFont file object to get position of sample24 chunk from * * Gets the position of the sample24 data chunk in a SoundFont file object. * * Returns: Position in the SoundFont file of the first byte of the * sample24 data chunk, in bytes */ guint ipatch_sf2_file_get_sample24_pos(IpatchSF2File *file) { g_return_val_if_fail(IPATCH_IS_SF2_FILE(file), 0); /* atomic read */ return (file->sample24_pos); } libinstpatch-1.1.6/libinstpatch/IpatchSF2File.h000066400000000000000000000072721400263525300213730ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_FILE_H__ #define __IPATCH_SF2_FILE_H__ #include #include #include #include /* forward type declarations */ typedef struct _IpatchSF2File IpatchSF2File; typedef struct _IpatchSF2FileClass IpatchSF2FileClass; typedef struct _IpatchSF2Phdr IpatchSF2Phdr; typedef struct _IpatchSF2Ihdr IpatchSF2Ihdr; typedef struct _IpatchSF2Shdr IpatchSF2Shdr; typedef struct _IpatchSF2Bag IpatchSF2Bag; #define IPATCH_TYPE_SF2_FILE (ipatch_sf2_file_get_type ()) #define IPATCH_SF2_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SF2_FILE, IpatchSF2File)) #define IPATCH_SF2_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SF2_FILE, \ IpatchSF2FileClass)) #define IPATCH_IS_SF2_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SF2_FILE)) #define IPATCH_IS_SF2_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SF2_FILE)) #define IPATCH_SF2_FILE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SF2_FILE, \ IpatchSF2FileClass)) /* SoundFont info IDs */ #define IPATCH_SFONT_FOURCC_IFIL IPATCH_FOURCC ('i','f','i','l') #define IPATCH_SFONT_FOURCC_ISNG IPATCH_FOURCC ('i','s','n','g') #define IPATCH_SFONT_FOURCC_INAM IPATCH_FOURCC ('I','N','A','M') #define IPATCH_SFONT_FOURCC_IROM IPATCH_FOURCC ('i','r','o','m') #define IPATCH_SFONT_FOURCC_IVER IPATCH_FOURCC ('i','v','e','r') #define IPATCH_SFONT_FOURCC_ICRD IPATCH_FOURCC ('I','C','R','D') #define IPATCH_SFONT_FOURCC_IENG IPATCH_FOURCC ('I','E','N','G') #define IPATCH_SFONT_FOURCC_IPRD IPATCH_FOURCC ('I','P','R','D') #define IPATCH_SFONT_FOURCC_ICOP IPATCH_FOURCC ('I','C','O','P') #define IPATCH_SFONT_FOURCC_ICMT IPATCH_FOURCC ('I','C','M','T') #define IPATCH_SFONT_FOURCC_ISFT IPATCH_FOURCC ('I','S','F','T') /** * IPATCH_SFONT_NAME_SIZE: (skip) */ #define IPATCH_SFONT_NAME_SIZE 20 /* name string size (Preset/Inst/Sample) */ /* SoundFont file object (derived from IpatchFile) */ struct _IpatchSF2File { IpatchFile parent_instance; guint32 sample_pos; /* position in file of the sample data */ guint32 sample_size; /* sample data chunk size (in samples) */ guint32 sample24_pos; /* position in file of LS bytes of 24 bit samples or 0 */ }; /* SoundFont file class (derived from IpatchFile) */ struct _IpatchSF2FileClass { IpatchFileClass parent_class; }; GType ipatch_sf2_file_get_type(void); IpatchSF2File *ipatch_sf2_file_new(void); void ipatch_sf2_file_set_sample_pos(IpatchSF2File *file, guint sample_pos); guint ipatch_sf2_file_get_sample_pos(IpatchSF2File *file); void ipatch_sf2_file_set_sample_size(IpatchSF2File *file, guint sample_size); guint ipatch_sf2_file_get_sample_size(IpatchSF2File *file); void ipatch_sf2_file_set_sample24_pos(IpatchSF2File *file, guint sample24_pos); guint ipatch_sf2_file_get_sample24_pos(IpatchSF2File *file); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2File_priv.h000066400000000000000000000106721400263525300224310ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_FILE_PRIV_H__ #define __IPATCH_SF2_FILE_PRIV_H__ /* SoundFont file preset header */ struct _IpatchSF2Phdr { char name[20]; /* preset name */ guint16 program; /* MIDI program number */ guint16 bank; /* MIDI bank number */ guint16 bag_index; /* index into preset bag (#IPFileBag) */ guint32 library; /* Not used (preserved) */ guint32 genre; /* Not used (preserved) */ guint32 morphology; /* Not used (preserved) */ }; /* SoundFont file instrument header */ struct _IpatchSF2Ihdr { char name[20]; /* name of instrument */ guint16 bag_index; /* instrument bag index (#IPFileBag) */ }; /* SoundFont file sample header */ struct _IpatchSF2Shdr { char name[20]; /* sample name */ guint32 start; /* offset to start of sample */ guint32 end; /* offset to end of sample */ guint32 loop_start; /* offset to start of loop */ guint32 loop_end; /* offset to end of loop */ guint32 rate; /* sample rate recorded at */ guint8 root_note; /* root midi note number */ gint8 fine_tune; /* pitch correction in cents */ guint16 link_index; /* linked sample index for stereo samples */ guint16 type; /* type of sample (see IpatchSF2SampleFlags) */ }; /* SoundFont file bag (zone), indexes for zone's generators and modulators */ struct _IpatchSF2Bag { guint16 mod_index; /* index into modulator list */ guint16 gen_index; /* index into generator list */ }; /* RIFF chunk FOURCC guint32 integers */ #define IPATCH_SFONT_FOURCC_SFBK IPATCH_FOURCC ('s','f','b','k') #define IPATCH_SFONT_FOURCC_INFO IPATCH_FOURCC ('I','N','F','O') #define IPATCH_SFONT_FOURCC_SDTA IPATCH_FOURCC ('s','d','t','a') #define IPATCH_SFONT_FOURCC_PDTA IPATCH_FOURCC ('p','d','t','a') #define IPATCH_SFONT_FOURCC_SMPL IPATCH_FOURCC ('s','m','p','l') #define IPATCH_SFONT_FOURCC_SM24 IPATCH_FOURCC ('s','m','2','4') #define IPATCH_SFONT_FOURCC_PHDR IPATCH_FOURCC ('p','h','d','r') #define IPATCH_SFONT_FOURCC_PBAG IPATCH_FOURCC ('p','b','a','g') #define IPATCH_SFONT_FOURCC_PMOD IPATCH_FOURCC ('p','m','o','d') #define IPATCH_SFONT_FOURCC_PGEN IPATCH_FOURCC ('p','g','e','n') #define IPATCH_SFONT_FOURCC_INST IPATCH_FOURCC ('i','n','s','t') #define IPATCH_SFONT_FOURCC_IBAG IPATCH_FOURCC ('i','b','a','g') #define IPATCH_SFONT_FOURCC_IMOD IPATCH_FOURCC ('i','m','o','d') #define IPATCH_SFONT_FOURCC_IGEN IPATCH_FOURCC ('i','g','e','n') #define IPATCH_SFONT_FOURCC_SHDR IPATCH_FOURCC ('s','h','d','r') /* SoundFont file chunk sizes */ #define IPATCH_SFONT_VERSION_SIZE 4 /* file version info size */ #define IPATCH_SFONT_PHDR_SIZE 38 /* file preset header size */ #define IPATCH_SFONT_INST_SIZE 22 /* file instrument header size */ #define IPATCH_SFONT_SHDR_SIZE 46 /* file sample header size */ #define IPATCH_SFONT_BAG_SIZE 4 /* file bag (zone) size */ #define IPATCH_SFONT_MOD_SIZE 10 /* file modulator size */ #define IPATCH_SFONT_GEN_SIZE 4 /* file generator size */ /** * IpatchSF2FileSampleType: * @IPATCH_SF2_FILE_SAMPLE_TYPE_MONO: Mono channel * @IPATCH_SF2_FILE_SAMPLE_TYPE_RIGHT: Right channel of a stereo pair * @IPATCH_SF2_FILE_SAMPLE_TYPE_LEFT: Left channel of a stereo pair * @IPATCH_SF2_FILE_SAMPLE_TYPE_LINKED: Linked list of samples (not yet used) * @IPATCH_SF2_FILE_SAMPLE_TYPE_ROM: A ROM sample * * SoundFont file sample channel mode */ typedef enum { IPATCH_SF2_FILE_SAMPLE_TYPE_MONO = 1 << 0, IPATCH_SF2_FILE_SAMPLE_TYPE_RIGHT = 1 << 1, IPATCH_SF2_FILE_SAMPLE_TYPE_LEFT = 1 << 2, IPATCH_SF2_FILE_SAMPLE_TYPE_LINKED = 1 << 3, IPATCH_SF2_FILE_SAMPLE_TYPE_ROM = 1 << 15 } IpatchSF2FileSampleType; #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2Gen.c000066400000000000000000000446551400263525300212260ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2Gen * @short_description: SoundFont generator functions and definitions * @see_also: * @stability: Stable * * SoundFont generators are synthesis parameters used by #IpatchSF2Preset, * #IpatchSF2Inst, #IpatchSF2PZone and #IpatchSF2IZone objects. */ #include #include #include /* for conversion functions */ #include #include #include "IpatchSF2Gen.h" #include "IpatchParamProp.h" #include "IpatchRange.h" #include "IpatchUnit.h" #include "ipatch_priv.h" #include "builtin_enums.h" #include "util.h" /* default arrays for offset and absolute (preset/instrument zones). For fast initialization of generator arrays. */ IpatchSF2GenArray *ipatch_sf2_gen_ofs_array = NULL; IpatchSF2GenArray *ipatch_sf2_gen_abs_array = NULL; /* masks of valid generators for offset or absolute arrays */ guint64 ipatch_sf2_gen_ofs_valid_mask; guint64 ipatch_sf2_gen_abs_valid_mask; /* a mask for generators that can be added together (ofs_mask ! ranges) */ guint64 ipatch_sf2_gen_add_mask; /* array of property names by generator ID */ static const char **gen_property_names = NULL; /* names [IPATCH_SF2_GEN_COUNT] */ /*----------------------------------------------------------------------------- Initialization/deinitialization of the generator subsystem ----------------------------------------------------------------------------*/ /** * _ipatch_sf2_gen_init: (skip) * * Library internal init function for SoundFont generator subsystem. */ void _ipatch_sf2_gen_init(void) { GEnumClass *enum_class; GEnumValue *enum_val; guint64 v; int i; /* initialize valid generator masks */ for(i = 0, v = 0x1; i < IPATCH_SF2_GEN_COUNT; i++, v <<= 1) { switch(i) { case IPATCH_SF2_GEN_SAMPLE_START: case IPATCH_SF2_GEN_SAMPLE_END: case IPATCH_SF2_GEN_SAMPLE_LOOP_START: case IPATCH_SF2_GEN_SAMPLE_LOOP_END: case IPATCH_SF2_GEN_SAMPLE_COARSE_START: case IPATCH_SF2_GEN_SAMPLE_COARSE_END: case IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START: case IPATCH_SF2_GEN_FIXED_NOTE: case IPATCH_SF2_GEN_FIXED_VELOCITY: case IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END: case IPATCH_SF2_GEN_SAMPLE_MODES: case IPATCH_SF2_GEN_EXCLUSIVE_CLASS: case IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE: ipatch_sf2_gen_abs_valid_mask |= v; /* OK for absolute generators */ break; case IPATCH_SF2_GEN_UNUSED1: case IPATCH_SF2_GEN_UNUSED2: case IPATCH_SF2_GEN_UNUSED3: case IPATCH_SF2_GEN_UNUSED4: case IPATCH_SF2_GEN_RESERVED1: case IPATCH_SF2_GEN_RESERVED2: case IPATCH_SF2_GEN_RESERVED3: case IPATCH_SF2_GEN_INSTRUMENT_ID: /* we don't use these in the API */ case IPATCH_SF2_GEN_SAMPLE_ID: /* they get saved to files though */ break; /* not valid for any generator type */ default: /* valid for either generator type */ ipatch_sf2_gen_ofs_valid_mask |= v; ipatch_sf2_gen_abs_valid_mask |= v; break; } } /* create generator add mask (gens that can be directly summed) */ ipatch_sf2_gen_add_mask = ipatch_sf2_gen_ofs_valid_mask; /* I don't trust constants to be 64 bit (LL perhaps?), so .. */ v = 1; v <<= IPATCH_SF2_GEN_NOTE_RANGE; ipatch_sf2_gen_add_mask &= ~v; v = 1; v <<= IPATCH_SF2_GEN_VELOCITY_RANGE; ipatch_sf2_gen_add_mask &= ~v; /* initialize default offset array values */ ipatch_sf2_gen_ofs_array = ipatch_sf2_gen_array_new(TRUE); ipatch_sf2_gen_ofs_array->values[IPATCH_SF2_GEN_NOTE_RANGE].range.low = 0; ipatch_sf2_gen_ofs_array->values[IPATCH_SF2_GEN_NOTE_RANGE].range.high = 127; ipatch_sf2_gen_ofs_array->values[IPATCH_SF2_GEN_VELOCITY_RANGE].range.low = 0; ipatch_sf2_gen_ofs_array->values[IPATCH_SF2_GEN_VELOCITY_RANGE].range.high = 127; /* initialize absolute generator default values */ ipatch_sf2_gen_abs_array = ipatch_sf2_gen_array_new(TRUE); for(i = 0; i < IPATCH_SF2_GEN_COUNT; i++) { ipatch_sf2_gen_abs_array->values[i] = ipatch_sf2_gen_info[i].def; } /* init flags to all valid generators for the given type */ ipatch_sf2_gen_ofs_array->flags = ipatch_sf2_gen_ofs_valid_mask; ipatch_sf2_gen_abs_array->flags = ipatch_sf2_gen_abs_valid_mask; /* initialize array mapping generator IDs to property names */ gen_property_names = g_malloc(sizeof(char *) * IPATCH_SF2_GEN_COUNT); enum_class = g_type_class_ref(IPATCH_TYPE_SF2_GEN_TYPE); /* ++ref */ if(log_if_fail(enum_class != NULL)) /* shouldn't happen.. just in case */ { for(i = 0; i < IPATCH_SF2_GEN_COUNT; i++) { gen_property_names[i] = NULL; } } else for(i = 0; i < IPATCH_SF2_GEN_COUNT; i++) { enum_val = g_enum_get_value(enum_class, i); gen_property_names[i] = enum_val ? enum_val->value_nick : NULL; } g_type_class_unref(enum_class); /* --ref */ } /** * _ipatch_sf2_gen_deinit: * * Feeing SoundFont generator subsystem. */ void _ipatch_sf2_gen_deinit (void) { g_free((gpointer)gen_property_names); ipatch_sf2_gen_array_free(ipatch_sf2_gen_ofs_array); ipatch_sf2_gen_array_free(ipatch_sf2_gen_abs_array); } /** * ipatch_sf2_gen_is_valid: * @genid: Generator ID to check * @propstype: Generator property type (instrument/preset + global) * * Checks if a generator is valid for the given @propstype. * * Returns: %TRUE if valid, %FALSE otherwise */ gboolean ipatch_sf2_gen_is_valid(guint genid, IpatchSF2GenPropsType propstype) { if(genid == IPATCH_SF2_GEN_SAMPLE_MODES && propstype == IPATCH_SF2_GEN_PROPS_INST_GLOBAL) { return (FALSE); } else if((propstype & 0x1) == IPATCH_SF2_GEN_PROPS_INST) { return ((ipatch_sf2_gen_abs_valid_mask & ((guint64)0x1 << genid)) != 0); } else { return ((ipatch_sf2_gen_ofs_valid_mask & ((guint64)0x1 << genid)) != 0); } } /* IpatchSF2GenArray boxed type */ GType ipatch_sf2_gen_array_get_type(void) { static GType type = 0; if(!type) type = g_boxed_type_register_static("IpatchSF2GenArray", (GBoxedCopyFunc)ipatch_sf2_gen_array_duplicate, (GBoxedFreeFunc)ipatch_sf2_gen_array_free); return (type); } /** * ipatch_sf2_gen_array_new: * @clear: If %TRUE then array will be cleared, %FALSE will not initalize it * and it is up to the caller to do so. * * Create a new generator array object. A convenience function really, * because one could just allocate an IpatchSF2GenArray structure instead. * * Returns: New generator array */ IpatchSF2GenArray * ipatch_sf2_gen_array_new(gboolean clear) { if(!clear) { return (g_new(IpatchSF2GenArray, 1)); } else { return (g_new0(IpatchSF2GenArray, 1)); } } /** * ipatch_sf2_gen_free: * @array: Generator array to free * * Frees a generator array structure. */ void ipatch_sf2_gen_array_free(IpatchSF2GenArray *array) { g_return_if_fail(array != NULL); g_free(array); } /** * ipatch_sf2_gen_array_duplicate: * @array: Generator array to duplicate * * Duplicates a generator array structure. * * Returns: A newly allocated generator array structure which is a duplicate * of @array. */ IpatchSF2GenArray * ipatch_sf2_gen_array_duplicate(const IpatchSF2GenArray *array) { IpatchSF2GenArray *new; g_return_val_if_fail(array != NULL, NULL); new = g_new(IpatchSF2GenArray, 1); memcpy(new, array, sizeof(IpatchSF2GenArray)); return (new); } /** * ipatch_sf2_gen_array_init: * @array: Generator array * @offset: %TRUE = initialize to Preset offset (zero) values, * %FALSE = initialize to instrument default values * @set: %TRUE to set flags indicating generator values are set, %FALSE to * clear all flag "set" bits * * Initialize a generator array to default values. */ void ipatch_sf2_gen_array_init(IpatchSF2GenArray *array, gboolean offset, gboolean set) { IpatchSF2GenArray *duparray; g_return_if_fail(array != NULL); duparray = offset ? ipatch_sf2_gen_ofs_array : ipatch_sf2_gen_abs_array; memcpy(array->values, duparray->values, sizeof(array->values)); if(set) { array->flags = duparray->flags; } else { array->flags = 0; } } /** * ipatch_sf2_gen_array_offset: * @abs_array: Destination generator amount array that contains absolute * (Instrument) generator values * @ofs_array: Source generator amount array that contains offset (Preset) * generator values * * Offsets the generators amount array in @abs_array by adding the * values in @ofs_array to it. Values are clamped to their valid ranges. * * Returns: %FALSE if note or velocity range does not intersect (in which case * the non-intersecting ranges are left unassigned), %TRUE otherwise */ gboolean ipatch_sf2_gen_array_offset(IpatchSF2GenArray *abs_array, const IpatchSF2GenArray *ofs_array) { IpatchSF2GenAmount *abs_vals; const IpatchSF2GenAmount *ofs_vals; gboolean retval; guint64 v; gint32 temp; int i; abs_vals = abs_array->values; ofs_vals = ofs_array->values; for(i = 0, v = 0x1; i < IPATCH_SF2_GEN_COUNT; i++, v <<= 1) { /* generator in add_mask and offset value set? */ if((ipatch_sf2_gen_add_mask & v) && (ofs_array->flags & v)) { temp = (gint32)(abs_vals[i].sword) + (gint32)(ofs_vals[i].sword); if(temp < (gint32)ipatch_sf2_gen_info[i].min.sword) { temp = ipatch_sf2_gen_info[i].min.sword; } else if(temp > (gint32)ipatch_sf2_gen_info[i].max.sword) { temp = ipatch_sf2_gen_info[i].max.sword; } abs_vals[i].sword = (gint16)temp; abs_array->flags |= v; /* generator now set */ } } retval = ipatch_sf2_gen_range_intersect(&abs_vals[IPATCH_SF2_GEN_NOTE_RANGE], &ofs_vals[IPATCH_SF2_GEN_NOTE_RANGE]); return retval && ipatch_sf2_gen_range_intersect(&abs_vals[IPATCH_SF2_GEN_VELOCITY_RANGE], &ofs_vals[IPATCH_SF2_GEN_VELOCITY_RANGE]); } /** * ipatch_sf2_gen_array_intersect_test: * @array1: First generator amount array * @array2: Second generator amount array * * Checks if the note and velocity ranges in two generator arrays intersect. * * Returns: %TRUE if both ranges intersect, %FALSE if one or both do not. */ gboolean ipatch_sf2_gen_array_intersect_test(const IpatchSF2GenArray *array1, const IpatchSF2GenArray *array2) { if(!ipatch_sf2_gen_range_intersect_test(&array1->values[IPATCH_SF2_GEN_NOTE_RANGE], &array2->values[IPATCH_SF2_GEN_NOTE_RANGE])) { return (FALSE); } return (ipatch_sf2_gen_range_intersect_test(&array1->values[IPATCH_SF2_GEN_VELOCITY_RANGE], &array2->values[IPATCH_SF2_GEN_VELOCITY_RANGE])); } /** * ipatch_sf2_gen_array_count_set: * @array: Generator array * * Get count of "set" generators in a generator array. * * Returns: Count of "set" generators. */ guint ipatch_sf2_gen_array_count_set(IpatchSF2GenArray *array) { guint count = 0; guint64 v; g_return_val_if_fail(array != NULL, 0); for(v = array->flags; v; v >>= 1) if(v & 0x1) { count++; } return (count); } /** * ipatch_sf2_gen_amount_to_value: * @genid: Generator ID * @amt: Generator amount for given @genid * @value: Uninitialized GValue to set to @amt * * Converts a generator amount to a GValue. Value will be initialized to * one of two types: G_TYPE_INT for signed/unsigned integers or * IPATCH_TYPE_RANGE for velocity or note split ranges. */ void ipatch_sf2_gen_amount_to_value(guint genid, const IpatchSF2GenAmount *amt, GValue *value) { g_return_if_fail(genid < IPATCH_SF2_GEN_COUNT); g_return_if_fail(amt != NULL); g_return_if_fail(value != NULL); if(ipatch_sf2_gen_info [genid].unit != IPATCH_UNIT_TYPE_RANGE) { g_value_init(value, G_TYPE_INT); g_value_set_int(value, amt->sword); } else { IpatchRange range; range.low = amt->range.low; range.high = amt->range.high; g_value_init(value, IPATCH_TYPE_RANGE); ipatch_value_set_range(value, &range); } } /** * ipatch_sf2_gen_default_value: * @genid: Generator ID * @ispreset: TRUE for preset generators, FALSE for instrument * @out_amt: (out): A pointer to store the default amount into * * Get default value for a generator ID for the specified (@ispreset) zone * type. */ void ipatch_sf2_gen_default_value(guint genid, gboolean ispreset, IpatchSF2GenAmount *out_amt) { g_return_if_fail(out_amt != NULL); out_amt->sword = 0; /* in case we fail, user gets 0 amount */ g_return_if_fail(ipatch_sf2_gen_is_valid(genid, ispreset)); if(ispreset) { if(ipatch_sf2_gen_info[genid].unit == IPATCH_UNIT_TYPE_RANGE) { out_amt->range.low = 0; out_amt->range.high = 127; } /* else: Amount already set to 0, which is default for preset gens */ } else { *out_amt = ipatch_sf2_gen_info[genid].def; } } /** * ipatch_sf2_gen_offset: * @genid: ID of Generator to offset. Must be a valid preset generator. * @dst: (inout): Pointer to the initial amount to offset, result is stored back * into this parameter. * @ofs: Pointer to offset amount. * * Offsets a generator amount. Result of offset is clamped to maximum and * minimum values for the given generator ID. In the case of note or * velocity ranges a return value of %TRUE (clamped) means that the ranges * don't intersect (contrary return value to other range related functions). * * Returns: %TRUE if value was clamped, %FALSE otherwise. */ gboolean ipatch_sf2_gen_offset(guint genid, IpatchSF2GenAmount *dst, const IpatchSF2GenAmount *ofs) { gint32 temp; gboolean clamped = FALSE; g_return_val_if_fail(dst != NULL, FALSE); g_return_val_if_fail(ofs != NULL, FALSE); g_return_val_if_fail(ipatch_sf2_gen_is_valid(genid, TRUE), FALSE); if(genid != IPATCH_SF2_GEN_NOTE_RANGE && genid != IPATCH_SF2_GEN_VELOCITY_RANGE) { temp = (gint32)(dst->sword) + (gint32)(ofs->sword); if(temp < (gint32)ipatch_sf2_gen_info[genid].min.sword) { temp = ipatch_sf2_gen_info[genid].min.sword; clamped = TRUE; } else if(temp > (gint32)ipatch_sf2_gen_info[genid].max.sword) { temp = ipatch_sf2_gen_info[genid].max.sword; clamped = TRUE; } dst->sword = (gint16)temp; } else { clamped = !ipatch_sf2_gen_range_intersect(dst, ofs); } return (clamped); } /** * ipatch_sf2_gen_clamp: * @genid: Generator ID (#IpatchSF2GenType) * @sfval: (inout): Generator value to clamp (changed in place) * @ispreset: TRUE if its a Preset generator, FALSE if Instrument * * Clamp a generators value to its valid range. */ void ipatch_sf2_gen_clamp(guint genid, int *sfval, gboolean ispreset) { int ofsrange; /* used only for offset gens, range of value */ g_return_if_fail(ipatch_sf2_gen_is_valid(genid, ispreset)); if(ispreset) { ofsrange = ipatch_sf2_gen_info[genid].max.sword - ipatch_sf2_gen_info[genid].min.sword; if(*sfval < -ofsrange) { *sfval = -ofsrange; } else if(*sfval > ofsrange) { *sfval = ofsrange; } } else { if(*sfval < ipatch_sf2_gen_info[genid].min.sword) { *sfval = ipatch_sf2_gen_info[genid].min.sword; } else if(*sfval > ipatch_sf2_gen_info[genid].max.sword) { *sfval = ipatch_sf2_gen_info[genid].max.sword; } } } /** * ipatch_sf2_gen_range_intersect: * @dst: (inout): First generator amount range, result is also stored here * @src: Second generator amount range * * Find intersection of two generator ranges (common shared range). * If ranges don't share anything in common @dst is not assigned. * * Returns: %FALSE if ranges don't share any range in common. */ gboolean ipatch_sf2_gen_range_intersect(IpatchSF2GenAmount *dst, const IpatchSF2GenAmount *src) { guint8 dl, dh, sl, sh; dl = dst->range.low; dh = dst->range.high; sl = src->range.low; sh = src->range.high; /* Nothing in common? */ if(dh < sl || sh < dl) { return (FALSE); } dst->range.low = MAX(dl, sl); dst->range.high = MIN(dh, sh); return (TRUE); } /** * ipatch_sf2_gen_range_intersect_test: * @amt1: First generator amount range * @amt2: Second generator amount range * * Test if two ranges intersect. * * Returns: %FALSE if ranges don't share any range in common, %TRUE otherwise */ gboolean ipatch_sf2_gen_range_intersect_test(const IpatchSF2GenAmount *amt1, const IpatchSF2GenAmount *amt2) { return (!(amt1->range.high < amt2->range.low || amt2->range.high < amt1->range.low)); } /** * ipatch_sf2_gen_get_prop_name: * @genid: Generator ID * * Get the GObject property name for a given generator ID. * * Returns: Property name or %NULL if no property name for @genid. * The returned string is internal and should not be modified or freed. */ G_CONST_RETURN char * ipatch_sf2_gen_get_prop_name(guint genid) { g_return_val_if_fail(genid < IPATCH_SF2_GEN_COUNT, NULL); return (gen_property_names[genid]); } libinstpatch-1.1.6/libinstpatch/IpatchSF2Gen.h000066400000000000000000000243761400263525300212310ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_GEN_H__ #define __IPATCH_SF2_GEN_H__ #include #include #include /* total number of generators */ #define IPATCH_SF2_GEN_COUNT 59 /* forward type declarations */ typedef union _IpatchSF2GenAmount IpatchSF2GenAmount; typedef struct _IpatchSF2GenArray IpatchSF2GenArray; typedef struct _IpatchSF2GenInfo IpatchSF2GenInfo; /* IpatchSF2GenArray has a glib boxed type */ #define IPATCH_TYPE_SF2_GEN_ARRAY (ipatch_sf2_gen_array_get_type ()) /** * IpatchSF2GenPropsType: * * Generator property type (defines which gens are valid and their ranges). * Note that TRUE/FALSE can be used to designate PRESET/INST (backwards * compatible with previous function). Also note that global properties can * be treated as a flag: #IPATCH_SF2_GEN_PROPS_GLOBAL_FLAG. */ typedef enum { IPATCH_SF2_GEN_PROPS_INST = 0, /* instrument "absolute" properties */ IPATCH_SF2_GEN_PROPS_PRESET = 1, /* preset "offset" properties */ IPATCH_SF2_GEN_PROPS_INST_GLOBAL = 2, /* inst properties with no sample link */ IPATCH_SF2_GEN_PROPS_PRESET_GLOBAL = 3 /* preset props with no inst link */ } IpatchSF2GenPropsType; /* can treat GLOBAL enums from IpatchSF2GenPropsType as a flag */ #define IPATCH_SF2_GEN_PROPS_GLOBAL_FLAG 0x02 /* mask of props type without global flag */ #define IPATCH_SF2_GEN_PROPS_MASK 0x01 /* Generator amount (effect parameter amount) */ union _IpatchSF2GenAmount { /*< public >*/ gint16 sword; /* signed 16 bit value */ guint16 uword; /* unsigned 16 bit value */ struct { guint8 low; /* low value of range */ guint8 high; /* high value of range */ } range; /* range values, low - high */ }; /* Generator (effect parameter) */ struct _IpatchSF2Gen { /*< public >*/ guint16 id; /* generator #IPGenType ID */ IpatchSF2GenAmount amount; /* generator value */ }; /* generator array */ struct _IpatchSF2GenArray { /*< public >*/ guint64 flags; /* 1 bit for each generator indicating if it is set */ IpatchSF2GenAmount values[IPATCH_SF2_GEN_COUNT]; /* gen values */ }; /* calculate the set bit value for a given generator ID */ #define IPATCH_SF2_GENID_SET(genid) ((guint64)0x1 << (genid)) /* macros for manipulating individual set flag bits in a generator array Note: These macros don't take into account multi-thread locking. */ #define IPATCH_SF2_GEN_ARRAY_TEST_FLAG(array, genid) \ (((array)->flags & ((guint64)0x1 << (genid))) != 0) #define IPATCH_SF2_GEN_ARRAY_SET_FLAG(array, genid) \ ((array)->flags |= ((guint64)0x1 << (genid))) #define IPATCH_SF2_GEN_ARRAY_CLEAR_FLAG(array, genid) \ ((array)->flags &= ~((guint64)0x1 << (genid))) /* generator (effect parameter) types */ typedef enum { IPATCH_SF2_GEN_SAMPLE_START = 0, /* sample start offset */ IPATCH_SF2_GEN_SAMPLE_END = 1, /* sample end offset */ IPATCH_SF2_GEN_SAMPLE_LOOP_START = 2,/* sample loop start offset */ IPATCH_SF2_GEN_SAMPLE_LOOP_END = 3, /* sample loop end offset */ IPATCH_SF2_GEN_SAMPLE_COARSE_START = 4, /* sample start coarse offset */ IPATCH_SF2_GEN_MOD_LFO_TO_PITCH = 5, /* modulation LFO to pitch */ IPATCH_SF2_GEN_VIB_LFO_TO_PITCH = 6, /* vibrato LFO to pitch */ IPATCH_SF2_GEN_MOD_ENV_TO_PITCH = 7, /* modulation envelope to pitch */ IPATCH_SF2_GEN_FILTER_CUTOFF = 8, /* initial filter cutoff */ IPATCH_SF2_GEN_FILTER_Q = 9, /* filter Q */ IPATCH_SF2_GEN_MOD_LFO_TO_FILTER_CUTOFF = 10, /* mod LFO to filter cutoff */ IPATCH_SF2_GEN_MOD_ENV_TO_FILTER_CUTOFF = 11, /* mod envelope to filter cutoff */ IPATCH_SF2_GEN_SAMPLE_COARSE_END = 12, /* sample end course offset */ IPATCH_SF2_GEN_MOD_LFO_TO_VOLUME = 13, /* modulation LFO to volume */ IPATCH_SF2_GEN_UNUSED1 = 14, IPATCH_SF2_GEN_CHORUS = 15, /* chorus */ IPATCH_SF2_GEN_REVERB = 16, /* reverb */ IPATCH_SF2_GEN_PAN = 17, /* panning */ IPATCH_SF2_GEN_UNUSED2 = 18, IPATCH_SF2_GEN_UNUSED3 = 19, IPATCH_SF2_GEN_UNUSED4 = 20, IPATCH_SF2_GEN_MOD_LFO_DELAY = 21, /* modulation LFO delay */ IPATCH_SF2_GEN_MOD_LFO_FREQ = 22, /* modulation LFO frequency */ IPATCH_SF2_GEN_VIB_LFO_DELAY = 23, /* vibrato LFO delay */ IPATCH_SF2_GEN_VIB_LFO_FREQ = 24, /* vibrato LFO frequency */ IPATCH_SF2_GEN_MOD_ENV_DELAY = 25, /* modulation envelope delay */ IPATCH_SF2_GEN_MOD_ENV_ATTACK = 26, /* modulation envelope attack */ IPATCH_SF2_GEN_MOD_ENV_HOLD = 27, /* modulation envelope hold */ IPATCH_SF2_GEN_MOD_ENV_DECAY = 28, /* modulation envelope decay */ IPATCH_SF2_GEN_MOD_ENV_SUSTAIN = 29, /* modulation envelope sustain */ IPATCH_SF2_GEN_MOD_ENV_RELEASE = 30, /* modulation envelope release */ IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_HOLD = 31, /* MIDI note to mod envelope hold */ IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_DECAY = 32, /* MIDI note to mod env decay */ IPATCH_SF2_GEN_VOL_ENV_DELAY = 33, /* volume envelope delay */ IPATCH_SF2_GEN_VOL_ENV_ATTACK = 34, /* volume envelope attack */ IPATCH_SF2_GEN_VOL_ENV_HOLD = 35, /* volume envelope hold */ IPATCH_SF2_GEN_VOL_ENV_DECAY = 36, /* volume envelope decay */ IPATCH_SF2_GEN_VOL_ENV_SUSTAIN = 37, /* volume envelope sustain */ IPATCH_SF2_GEN_VOL_ENV_RELEASE = 38, /* volume envelope release */ IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_HOLD = 39, /* MIDI note to vol envelope hold */ IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_DECAY = 40, /* MIDI note to volume env decay */ IPATCH_SF2_GEN_INSTRUMENT_ID = 41, /* instrument ID */ IPATCH_SF2_GEN_RESERVED1 = 42, IPATCH_SF2_GEN_NOTE_RANGE = 43, /* note range */ IPATCH_SF2_GEN_VELOCITY_RANGE = 44, /* note on velocity range */ IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START = 45, /* sample coarse loop start */ IPATCH_SF2_GEN_FIXED_NOTE = 46, /* MIDI fixed note */ IPATCH_SF2_GEN_FIXED_VELOCITY = 47, /* MIDI fixed velocity */ IPATCH_SF2_GEN_ATTENUATION = 48, /* initial volume attenuation */ IPATCH_SF2_GEN_RESERVED2 = 49, IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END = 50, /* sample end loop course ofs */ IPATCH_SF2_GEN_COARSE_TUNE = 51, /* course tuning */ IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE = 52, /* fine tune override */ IPATCH_SF2_GEN_SAMPLE_ID = 53, /* sample ID */ IPATCH_SF2_GEN_SAMPLE_MODES = 54, /* sample flags (IpatchSF2GenSampleModes)*/ IPATCH_SF2_GEN_RESERVED3 = 55, IPATCH_SF2_GEN_SCALE_TUNE = 56, /* scale tuning (tuning per MIDI note) */ IPATCH_SF2_GEN_EXCLUSIVE_CLASS = 57, /* exclusive class (only 1 at a time) */ IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE = 58 /* root note override */ } IpatchSF2GenType; /* Flags for IPATCH_SF2_GEN_SAMPLE_MODES generator sfspec24.pdf - p 36 - generator sampleModes (54) */ typedef enum { IPATCH_SF2_GEN_SAMPLE_MODE_NOLOOP, /* no loop */ IPATCH_SF2_GEN_SAMPLE_MODE_LOOP, /* standard loop */ /* not used. Should be interpreted as "no loop" */ IPATCH_SF2_GEN_SAMPLE_MODE_UNUSED, /* loop during the depression of the key, then plays the remainder of the sample. */ IPATCH_SF2_GEN_SAMPLE_MODE_LOOP_RELEASE } IpatchSF2GenSampleModes; /* generator info and constraints structure */ struct _IpatchSF2GenInfo { /*< public >*/ IpatchSF2GenAmount min; /* minimum value allowed */ IpatchSF2GenAmount max; /* maximum value allowed */ IpatchSF2GenAmount def; /* default value */ gint16 unit; /* #IpatchUnitType type */ char *label; /* short descriptive label */ char *descr; /* more complete description */ }; extern IpatchSF2GenArray *ipatch_sf2_gen_ofs_array; extern IpatchSF2GenArray *ipatch_sf2_gen_abs_array; extern guint64 ipatch_sf2_gen_ofs_valid_mask; extern guint64 ipatch_sf2_gen_abs_valid_mask; extern guint64 ipatch_sf2_gen_add_mask; /* Useful when libinstpatch library is used as a static library. */ extern const IpatchSF2GenInfo ipatch_sf2_gen_info[]; /* IpatchSF2Gen_tables.c */ /* Getter function returning ipatch_sf2_gen_info table. Useful when libinstpatch library is used as a shared library linked at load time. */ const IpatchSF2GenInfo *ipatch_sf2_get_gen_info(void); gboolean ipatch_sf2_gen_is_valid(guint genid, IpatchSF2GenPropsType propstype); GType ipatch_sf2_gen_array_get_type(void); IpatchSF2GenArray *ipatch_sf2_gen_array_new(gboolean clear); void ipatch_sf2_gen_array_free(IpatchSF2GenArray *genarray); IpatchSF2GenArray * ipatch_sf2_gen_array_duplicate(const IpatchSF2GenArray *array); void ipatch_sf2_gen_array_init(IpatchSF2GenArray *array, gboolean offset, gboolean set); gboolean ipatch_sf2_gen_array_offset(IpatchSF2GenArray *abs_array, const IpatchSF2GenArray *ofs_array); gboolean ipatch_sf2_gen_array_intersect_test(const IpatchSF2GenArray *array1, const IpatchSF2GenArray *array2); guint ipatch_sf2_gen_array_count_set(IpatchSF2GenArray *array); void ipatch_sf2_gen_amount_to_value(guint genid, const IpatchSF2GenAmount *amt, GValue *value); void ipatch_sf2_gen_default_value(guint genid, gboolean ispreset, IpatchSF2GenAmount *out_amt); gboolean ipatch_sf2_gen_offset(guint genid, IpatchSF2GenAmount *dst, const IpatchSF2GenAmount *ofs); void ipatch_sf2_gen_clamp(guint genid, int *sfval, gboolean ispreset); gboolean ipatch_sf2_gen_range_intersect(IpatchSF2GenAmount *dst, const IpatchSF2GenAmount *src); gboolean ipatch_sf2_gen_range_intersect_test(const IpatchSF2GenAmount *amt1, const IpatchSF2GenAmount *amt2); G_CONST_RETURN char *ipatch_sf2_gen_get_prop_name(guint genid); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2GenItem.c000066400000000000000000001034271400263525300220360ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2GenItem * @short_description: SoundFont generator item interface * @see_also: #IpatchSF2Preset, #IpatchSF2Inst, #IpatchSF2PZone, #IpatchSF2IZone * @stability: Stable * * Provides an interface for items which have SoundFont generator properties. * SoundFont generators are synthesis parameters used by #IpatchSF2Preset, * #IpatchSF2Inst, #IpatchSF2PZone and #IpatchSF2IZone objects. */ #include #include #include /* for conversion functions */ #include #include #include "IpatchSF2GenItem.h" #include "IpatchSF2Gen.h" #include "IpatchParamProp.h" #include "IpatchRange.h" #include "IpatchUnit.h" #include "ipatch_priv.h" #include "builtin_enums.h" #include "util.h" static gboolean ipatch_sf2_gen_item_set_gen_flag_no_notify(IpatchSF2GenItem *item, guint genid, gboolean setflag); /* non realtime synthesis parameters */ static const guint8 non_realtime[] = { IPATCH_SF2_GEN_SAMPLE_START, IPATCH_SF2_GEN_SAMPLE_END, IPATCH_SF2_GEN_SAMPLE_COARSE_START, IPATCH_SF2_GEN_SAMPLE_COARSE_END, IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_HOLD, IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_DECAY, IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_HOLD, IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_DECAY, IPATCH_SF2_GEN_INSTRUMENT_ID, IPATCH_SF2_GEN_NOTE_RANGE, IPATCH_SF2_GEN_VELOCITY_RANGE, IPATCH_SF2_GEN_FIXED_NOTE, IPATCH_SF2_GEN_FIXED_VELOCITY, IPATCH_SF2_GEN_SAMPLE_ID, IPATCH_SF2_GEN_SAMPLE_MODES, IPATCH_SF2_GEN_EXCLUSIVE_CLASS, IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE }; GType ipatch_sf2_gen_item_get_type(void) { static GType itype = 0; if(!itype) { static const GTypeInfo info = { sizeof(IpatchSF2GenItemIface), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) NULL, (GClassFinalizeFunc) NULL }; itype = g_type_register_static(G_TYPE_INTERFACE, "IpatchSF2GenItem", &info, 0); /* IpatchSF2GenItemIface types must be IpatchItem objects (for locking) */ g_type_interface_add_prerequisite(itype, IPATCH_TYPE_ITEM); } return (itype); } /** * ipatch_sf2_gen_item_get_amount: * @item: Item with generators to get value from * @genid: Generator ID (#IpatchSF2GenType) of value to get * @out_amt: (out): Pointer to store generator amount to * * Get a generator amount from an item with generator properties. * * Returns: %TRUE if generator value is set, %FALSE if not set, in which case * the value stored to output_amt is the default value for the given generator * ID. */ gboolean ipatch_sf2_gen_item_get_amount(IpatchSF2GenItem *item, guint genid, IpatchSF2GenAmount *out_amt) { IpatchSF2GenItemIface *iface; IpatchSF2GenArray *genarray; gboolean set; g_return_val_if_fail(IPATCH_IS_SF2_GEN_ITEM(item), FALSE); g_return_val_if_fail(genid < IPATCH_SF2_GEN_COUNT, FALSE); g_return_val_if_fail(out_amt != NULL, FALSE); /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */ iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item); g_return_val_if_fail(iface->genarray_ofs != 0, FALSE); genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs); IPATCH_ITEM_RLOCK(item); *out_amt = genarray->values[genid]; set = IPATCH_SF2_GEN_ARRAY_TEST_FLAG(genarray, genid); IPATCH_ITEM_RUNLOCK(item); return (set); } /** * ipatch_sf2_gen_item_set_amount: * @item: Item with generators to set value in * @genid: Generator ID (#IpatchSF2GenType) of generator to set * @amt: Value to set generator to * * Set a generator amount for an item with generators. * * #IpatchItem property notify is done for the property and possibly the "-set" * property if it was unset before. */ void ipatch_sf2_gen_item_set_amount(IpatchSF2GenItem *item, guint genid, IpatchSF2GenAmount *amt) { IpatchSF2GenItemIface *iface; IpatchSF2GenArray *genarray; IpatchSF2GenType propstype; GParamSpec *pspec; IpatchSF2GenAmount oldamt; GValue oldval = { 0 }, newval = { 0 }; gboolean valchanged = FALSE, oldset; g_return_if_fail(IPATCH_IS_ITEM(item)); g_return_if_fail(amt != NULL); iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item); propstype = iface->propstype; /* propstype for this class */ g_return_if_fail(ipatch_sf2_gen_is_valid(genid, propstype)); /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */ g_return_if_fail(iface->genarray_ofs != 0); genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs); IPATCH_ITEM_WLOCK(item); /* has different value? */ if(genarray->values[genid].sword != amt->sword) { oldamt = genarray->values[genid]; /* store old val for notify */ genarray->values[genid] = *amt; valchanged = TRUE; } oldset = IPATCH_SF2_GEN_ARRAY_TEST_FLAG(genarray, genid); IPATCH_SF2_GEN_ARRAY_SET_FLAG(genarray, genid); /* value is set */ IPATCH_ITEM_WUNLOCK(item); if(valchanged) /* do the property change notify if it actually changed */ { pspec = iface->specs[genid]; ipatch_sf2_gen_amount_to_value(genid, amt, &newval); ipatch_sf2_gen_amount_to_value(genid, &oldamt, &oldval); ipatch_item_prop_notify(IPATCH_ITEM(item), pspec, &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } if(oldset != TRUE) /* "set" state of property changed? */ { pspec = iface->setspecs[genid]; ipatch_item_prop_notify(IPATCH_ITEM(item), pspec, ipatch_util_value_bool_true, ipatch_util_value_bool_false); } } /** * ipatch_sf2_gen_item_set_gen_flag: * @item: Item with generator properties to set value of a gen "set" flag of * @genid: Generator ID (#IpatchSF2GenType) of generator to set "set" flag value of * @setflag: If %TRUE then generator amount is assigned, FALSE will cause the * amount to be unset (and revert to its default value) * * Sets the value of a generator "set" flag in an item with generators. * * #IpatchItem property notify is done for the property and possibly the "-set" * property if it was set before. */ void ipatch_sf2_gen_item_set_gen_flag(IpatchSF2GenItem *item, guint genid, gboolean setflag) { IpatchSF2GenItemIface *iface; GParamSpec *pspec; if(!ipatch_sf2_gen_item_set_gen_flag_no_notify(item, genid, setflag)) { return; } iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item); g_return_if_fail(iface != NULL); /* do "-set" property notify */ pspec = iface->setspecs[genid]; ipatch_item_prop_notify(IPATCH_ITEM(item), pspec, IPATCH_UTIL_VALUE_BOOL(setflag), IPATCH_UTIL_VALUE_BOOL(!setflag)); } /* Like ipatch_sf2_gen_item_set_gen_flag() but doesn't do "-set" property notify. * A regular property notify may occur though, * if the effective amount has changed. Caller can check if "-set" parameter * changed from return value (TRUE if changed from old value). */ static gboolean ipatch_sf2_gen_item_set_gen_flag_no_notify(IpatchSF2GenItem *item, guint genid, gboolean setflag) { IpatchSF2GenItemIface *iface; IpatchSF2GenArray *genarray; IpatchSF2GenType propstype; GParamSpec *pspec; IpatchSF2GenAmount oldamt, defamt; GValue newval = { 0 }, oldval = { 0 }; gboolean valchanged = FALSE, oldset; g_return_val_if_fail(IPATCH_IS_SF2_GEN_ITEM(item), FALSE); iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item); propstype = iface->propstype; /* propstype for this class */ g_return_val_if_fail(ipatch_sf2_gen_is_valid(genid, propstype), FALSE); /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */ g_return_val_if_fail(iface->genarray_ofs != 0, FALSE); genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs); /* grab default val from gen info table if absolute instrument gen or a range offset preset gen, otherwise offset gens are 0 by default */ if(!setflag) { if((propstype & 0x1) == IPATCH_SF2_GEN_PROPS_INST || ipatch_sf2_gen_info[genid].unit == IPATCH_UNIT_TYPE_RANGE) { defamt = ipatch_sf2_gen_info[genid].def; } else { defamt.sword = 0; } } IPATCH_ITEM_WLOCK(item); /* unsetting flag and amount has different value than default? */ if(!setflag && genarray->values[genid].sword != defamt.sword) { oldamt = genarray->values[genid]; genarray->values[genid] = defamt; valchanged = TRUE; } oldset = IPATCH_SF2_GEN_ARRAY_TEST_FLAG(genarray, genid); /* set/unset flag as requested */ if(setflag) { IPATCH_SF2_GEN_ARRAY_SET_FLAG(genarray, genid); } else { IPATCH_SF2_GEN_ARRAY_CLEAR_FLAG(genarray, genid); } IPATCH_ITEM_WUNLOCK(item); /* do the property change notify if it actually changed */ if(valchanged) { pspec = iface->specs[genid]; ipatch_sf2_gen_amount_to_value(genid, &defamt, &newval); ipatch_sf2_gen_amount_to_value(genid, &oldamt, &oldval); ipatch_item_prop_notify(IPATCH_ITEM(item), pspec, &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } return (setflag != oldset); } /** * ipatch_sf2_gen_item_count_set: * @item: Item with generators * * Get count of "set" generators in an item with generators. * * Returns: Count of "set" generators. */ guint ipatch_sf2_gen_item_count_set(IpatchSF2GenItem *item) { IpatchSF2GenItemIface *iface; IpatchSF2GenArray *genarray; guint count = 0; guint64 v; g_return_val_if_fail(IPATCH_IS_SF2_GEN_ITEM(item), 0); /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */ iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item); g_return_val_if_fail(iface->genarray_ofs != 0, 0); genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs); IPATCH_ITEM_RLOCK(item); for(v = genarray->flags; v; v >>= 1) if(v & 0x1) { count++; } IPATCH_ITEM_RUNLOCK(item); return (count); } /** * ipatch_sf2_gen_item_copy_all: * @item: Item with generators * @array: (out): Destination generator array to store to * * Copies an item's generators to a caller supplied generator array. */ void ipatch_sf2_gen_item_copy_all(IpatchSF2GenItem *item, IpatchSF2GenArray *array) { IpatchSF2GenItemIface *iface; IpatchSF2GenArray *genarray; g_return_if_fail(IPATCH_IS_SF2_GEN_ITEM(item)); g_return_if_fail(array != NULL); /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */ iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item); g_return_if_fail(iface->genarray_ofs != 0); genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs); IPATCH_ITEM_RLOCK(item); memcpy(array, genarray, sizeof(IpatchSF2GenArray)); IPATCH_ITEM_RUNLOCK(item); } /** * ipatch_sf2_gen_item_copy_set: * @item: Item with generators * @array: (out): Destination generator array to store to * * Copies a item's "set" generators to a caller supplied generator array. * This function differs from ipatch_sf2_gen_item_copy_all() in that it * only copies generators that are set. It can be used to override values * in one array with set values in another. Note that this doesn't change * any generators in @item, despite "set" being in the name. */ void ipatch_sf2_gen_item_copy_set(IpatchSF2GenItem *item, IpatchSF2GenArray *array) { IpatchSF2GenItemIface *iface; IpatchSF2GenArray *genarray; IpatchSF2GenAmount *vals; guint64 v; int i; g_return_if_fail(IPATCH_IS_SF2_GEN_ITEM(item)); g_return_if_fail(array != NULL); /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */ iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item); g_return_if_fail(iface->genarray_ofs != 0); genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs); IPATCH_ITEM_RLOCK(item); vals = genarray->values; v = genarray->flags; array->flags |= v; /* set destination array bits from source */ for(i = 0; v != 0; i++, v >>= 1) if(v & 0x1) { array->values[i] = vals[i]; /* only copy set values */ } IPATCH_ITEM_RUNLOCK(item); } /** * ipatch_sf2_gen_item_set_note_range: * @item: Item with generators * @low: Low value of range (MIDI note # between 0 and 127) * @high: High value of range (MIDI note # between 0 and 127) * * Set the MIDI note range that an item with generators is active on. * Only a convenience function really. */ void ipatch_sf2_gen_item_set_note_range(IpatchSF2GenItem *item, int low, int high) { IpatchSF2GenAmount amt; g_return_if_fail(IPATCH_IS_SF2_GEN_ITEM(item)); g_return_if_fail(low >= 0 && low <= 127); g_return_if_fail(high >= 0 && high <= 127); if(low > high) /* swap if backwards */ { int temp = low; low = high; high = temp; } amt.range.low = low; amt.range.high = high; ipatch_sf2_gen_item_set_amount(item, IPATCH_SF2_GEN_NOTE_RANGE, &amt); } /** * ipatch_sf2_gen_item_set_velocity_range: * @item: Item with generators * @low: Low value of range (MIDI velocity # between 0 and 127) * @high: High value of range (MIDI velocity # between 0 and 127) * * Set the MIDI velocity range that an item with generators is active on. * Only a convenience function really. */ void ipatch_sf2_gen_item_set_velocity_range(IpatchSF2GenItem *item, int low, int high) { IpatchSF2GenAmount amt; g_return_if_fail(IPATCH_IS_SF2_GEN_ITEM(item)); g_return_if_fail(low >= 0 && low <= 127); g_return_if_fail(high >= 0 && high <= 127); if(low > high) /* swap if backwards */ { int temp = low; low = high; high = temp; } amt.range.low = low; amt.range.high = high; ipatch_sf2_gen_item_set_amount(item, IPATCH_SF2_GEN_VELOCITY_RANGE, &amt); } /** * ipatch_sf2_gen_item_in_range: * @item: Item with generators * @note: MIDI note number or -1 for wildcard * @velocity: MIDI velocity or -1 for wildcard * * Check if a note and velocity falls in the ranges of an item with generators * * Returns: %TRUE if @item is in @note and @velocity range, %FALSE otherwise */ gboolean ipatch_sf2_gen_item_in_range(IpatchSF2GenItem *item, int note, int velocity) { IpatchSF2GenAmount *noteamt, *velamt; IpatchSF2GenItemIface *iface; IpatchSF2GenArray *genarray; gboolean in_range; g_return_val_if_fail(IPATCH_IS_SF2_GEN_ITEM(item), FALSE); /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */ iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item); g_return_val_if_fail(iface->genarray_ofs != 0, 0); genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs); IPATCH_ITEM_RLOCK(item); noteamt = &genarray->values[IPATCH_SF2_GEN_NOTE_RANGE]; velamt = &genarray->values[IPATCH_SF2_GEN_VELOCITY_RANGE]; in_range = ((note == -1) || (note >= noteamt->range.low && note <= noteamt->range.high)) && ((velocity == -1) || (velocity >= velamt->range.low && velocity <= velamt->range.high)); IPATCH_ITEM_RUNLOCK(item); return (in_range); } /** * ipatch_sf2_gen_item_intersect_test: * @item: Item with generators * @genarray: Generator array to test note and velocity ranges against * * Check if a given item's note and velocity ranges intersect with those in a * generator array. * * Returns: %TRUE if both note and velocity ranges intersect, %FALSE if one or * both do not. */ gboolean ipatch_sf2_gen_item_intersect_test(IpatchSF2GenItem *item, const IpatchSF2GenArray *genarray) { IpatchSF2GenAmount noteamt, velamt; IpatchSF2GenItemIface *iface; IpatchSF2GenArray *itemgenarray; g_return_val_if_fail(IPATCH_IS_SF2_GEN_ITEM(item), FALSE); /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */ iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item); g_return_val_if_fail(iface->genarray_ofs != 0, 0); itemgenarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs); IPATCH_ITEM_RLOCK(item); noteamt = itemgenarray->values[IPATCH_SF2_GEN_NOTE_RANGE]; velamt = itemgenarray->values[IPATCH_SF2_GEN_VELOCITY_RANGE]; IPATCH_ITEM_RUNLOCK(item); return ipatch_sf2_gen_range_intersect_test(¬eamt, &genarray->values[IPATCH_SF2_GEN_NOTE_RANGE]) && ipatch_sf2_gen_range_intersect_test(&velamt, &genarray->values[IPATCH_SF2_GEN_VELOCITY_RANGE]); } /** * ipatch_sf2_gen_item_class_get_pspec: (skip) * @genid: Generator ID * @klass: Class with an #IpatchSF2GenItem interface * * Get the parameter specification for a given generator ID and object class. * * Returns: (transfer none): The parameter specification for the generator or %NULL if * the given @genid for @klass is not valid. */ GParamSpec * ipatch_sf2_gen_item_class_get_pspec(GObjectClass *klass, guint genid) { IpatchSF2GenItemIface *gen_item_iface; g_return_val_if_fail(genid < IPATCH_SF2_GEN_COUNT, NULL); g_return_val_if_fail(klass != NULL, NULL); gen_item_iface = g_type_interface_peek(klass, IPATCH_TYPE_SF2_GEN_ITEM); g_return_val_if_fail(gen_item_iface != NULL, NULL); return (gen_item_iface->specs[genid]); } /** * ipatch_sf2_gen_item_class_get_pspec_set: (skip) * @genid: Generator ID * @klass: Class with an #IpatchSF2GenItem interface * * Get a "-set" property parameter specification for a given generator ID and * object class. * * Returns: (transfer none): The "-set" property parameter specification for the generator or * %NULL if the given @genid or @klass are not valid. */ GParamSpec * ipatch_sf2_gen_item_class_get_pspec_set(GObjectClass *klass, guint genid) { IpatchSF2GenItemIface *gen_item_iface; g_return_val_if_fail(genid < IPATCH_SF2_GEN_COUNT, NULL); g_return_val_if_fail(klass != NULL, NULL); gen_item_iface = g_type_interface_peek(klass, IPATCH_TYPE_SF2_GEN_ITEM); g_return_val_if_fail(gen_item_iface != NULL, NULL); return (gen_item_iface->setspecs[genid]); } /** * ipatch_sf2_gen_item_iface_install_properties: (skip) * @klass: Object class to install properties on * @propstype: Type of properties to install (instrument/preset) * @specs: Location to store a pointer to an allocated array of parameter * specs which should get copied to the interface's specs field and then freed * @setspecs: Location to store a pointer to an allocated array of parameter * specs which should get copied to the interface's setspecs field and then freed * * Installs generator item properties on the provided @klass. * Used internally in IpatchSF2GenItemIface init functions. */ /* This function is complicated by the fact that GObject properties are supposed * to be installed in the class init function, but the gen item interface has * not yet been initialized, so values need to be passed to the interface init * function. */ void ipatch_sf2_gen_item_iface_install_properties(GObjectClass *klass, IpatchSF2GenPropsType propstype, GParamSpec ***specs, GParamSpec ***setspecs) { GEnumClass *enum_class; GEnumValue *enum_value; GParamSpec *pspec; char *set_name; const IpatchSF2GenInfo *gen_info; int nonrt_index = 0; /* non realtime generator array index */ gboolean ispreset; int i, diff, unit; ispreset = propstype & 1; /* get generator type GObject enum */ enum_class = g_type_class_ref(IPATCH_TYPE_SF2_GEN_TYPE); /* ++ref */ g_return_if_fail(enum_class != NULL); *specs = g_new(GParamSpec *, IPATCH_SF2_GEN_COUNT); *setspecs = g_new(GParamSpec *, IPATCH_SF2_GEN_COUNT); /* install generator properties */ for(i = 0; i < IPATCH_SF2_GEN_COUNT; i++) { /* gen is valid for zone type? */ if(!ipatch_sf2_gen_is_valid(i, propstype)) { continue; } gen_info = &ipatch_sf2_gen_info[i]; enum_value = g_enum_get_value(enum_class, i); if(gen_info->unit == IPATCH_UNIT_TYPE_RANGE) pspec = ipatch_param_spec_range(enum_value->value_nick, _(gen_info->label), _(gen_info->descr ? gen_info->descr : gen_info->label), 0, 127, 0, 127, G_PARAM_READWRITE); /* allow 30 bit number which stores fine and coarse (32k) values */ else if(gen_info->unit == IPATCH_UNIT_TYPE_SAMPLES) pspec = g_param_spec_int(enum_value->value_nick, _(gen_info->label), _(gen_info->descr ? gen_info->descr : gen_info->label), ispreset ? -0x03FFFFFFF : 0, 0x03FFFFFFF, 0, G_PARAM_READWRITE); else if(!ispreset) /* integer absolute property */ pspec = g_param_spec_int(enum_value->value_nick, _(gen_info->label), _(gen_info->descr ? gen_info->descr : gen_info->label), gen_info->min.sword, gen_info->max.sword, gen_info->def.sword, G_PARAM_READWRITE); else /* integer offset property */ { diff = (int)gen_info->max.sword - gen_info->min.sword; pspec = g_param_spec_int(enum_value->value_nick, _(gen_info->label), _(gen_info->descr ? gen_info->descr : gen_info->label), -diff, diff, 0, G_PARAM_READWRITE); } /* all generators affect synthesis */ pspec->flags |= IPATCH_PARAM_SYNTH; /* if generator is not in non_realtime generator array.. */ if(nonrt_index >= G_N_ELEMENTS(non_realtime) || non_realtime[nonrt_index] != i) { pspec->flags |= IPATCH_PARAM_SYNTH_REALTIME; /* set realtime flag */ } else if(non_realtime[nonrt_index] == i) { nonrt_index++; /* current gen is non realtime, adv to next index */ } /* install the property */ g_object_class_install_property(klass, i + IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID, pspec); unit = gen_info->unit; /* set parameter unit type extended property */ if(ispreset) { if(unit == IPATCH_UNIT_TYPE_SF2_ABS_PITCH) { unit = IPATCH_UNIT_TYPE_SF2_OFS_PITCH; } else if(unit == IPATCH_UNIT_TYPE_SF2_ABS_TIME) { unit = IPATCH_UNIT_TYPE_SF2_OFS_TIME; } } ipatch_param_set(pspec, "unit-type", unit, NULL); (*specs)[i] = g_param_spec_ref(pspec); /* add to parameter spec array */ /* create prop-set property and add to setspecs array */ set_name = g_strconcat(enum_value->value_nick, "-set", NULL); pspec = g_param_spec_boolean(set_name, NULL, NULL, FALSE, G_PARAM_READWRITE); g_free(set_name); (*setspecs)[i] = g_param_spec_ref(pspec); /* add to set spec array */ /* install "-set" property */ g_object_class_install_property(klass, i + IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID, pspec); } /* for loop */ g_type_class_unref(enum_class); /* --ref */ } /** * ipatch_sf2_gen_item_iface_set_property: (skip) * @item: IpatchItem instance with generator properties * @property_id: Property id to set * @value: Value to set property to * * Used internally for classes with generators, to set values thereof. * * Returns: %TRUE if @property_id handled, %FALSE otherwise */ gboolean ipatch_sf2_gen_item_iface_set_property(IpatchSF2GenItem *item, guint property_id, const GValue *value) { IpatchSF2GenItemIface *iface; IpatchSF2GenArray *genarray; const IpatchSF2GenInfo *gen_info; IpatchSF2GenAmount amt; IpatchRange *range; int genid, coarse, val; gboolean oldset, oldcoarseset = 0, newcoarseset = 0; IpatchSF2GenAmount oldcoarseamt, newcoarseamt; gboolean coarsevalchanged = FALSE; GParamSpec *pspec; GValue newval = { 0 }, oldval = { 0 }; gboolean setflag; iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item); /* a "-set" property? */ if(property_id >= IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID && property_id < IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID + IPATCH_SF2_GEN_COUNT) { genid = property_id - IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID; /* generator valid for zone type? */ if(!ipatch_sf2_gen_is_valid(genid, iface->propstype)) { return (FALSE); } setflag = g_value_get_boolean(value); ipatch_sf2_gen_item_set_gen_flag_no_notify(item, genid, setflag); return (TRUE); } /* regular generator property? */ if(property_id < IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID || property_id >= IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID + IPATCH_SF2_GEN_COUNT) { return (FALSE); } genid = property_id - IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID; /* generator valid for zone type? */ if(!ipatch_sf2_gen_is_valid(genid, iface->propstype)) { return (FALSE); } /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */ g_return_val_if_fail(iface->genarray_ofs != 0, FALSE); genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs); gen_info = &ipatch_sf2_gen_info [genid]; if(gen_info->unit == IPATCH_UNIT_TYPE_SAMPLES) { /* set 2 generators - fine and coarse (32k) sample values */ if(genid == IPATCH_SF2_GEN_SAMPLE_START) { coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_START; } else if(genid == IPATCH_SF2_GEN_SAMPLE_END) { coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_END; } else if(genid == IPATCH_SF2_GEN_SAMPLE_LOOP_START) { coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START; } else if(genid == IPATCH_SF2_GEN_SAMPLE_LOOP_END) { coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END; } else { g_return_val_if_fail(NOT_REACHED, FALSE); } val = g_value_get_int(value); newcoarseamt.uword = val >> 15; IPATCH_ITEM_WLOCK(item); /* atomically set both gens */ /* prop notify done by IpatchItem methods, so just set value */ genarray->values[genid].uword = val & 0x7FFF; oldset = IPATCH_SF2_GEN_ARRAY_TEST_FLAG(genarray, genid); IPATCH_SF2_GEN_ARRAY_SET_FLAG(genarray, genid); /* value is set */ /* only set coarse value if it has changed */ if(genarray->values[coarse].uword != newcoarseamt.uword) { oldcoarseamt = genarray->values[coarse]; genarray->values[coarse] = newcoarseamt; coarsevalchanged = TRUE; oldcoarseset = IPATCH_SF2_GEN_ARRAY_TEST_FLAG(genarray, genid); if(val != 0) { IPATCH_SF2_GEN_ARRAY_SET_FLAG(genarray, genid); /* value is set */ newcoarseset = 1; } else { IPATCH_SF2_GEN_ARRAY_CLEAR_FLAG(genarray, genid); /* value is unset */ newcoarseset = 0; } } IPATCH_ITEM_WUNLOCK(item); if(oldset != TRUE) /* "set" state of property changed? */ { pspec = iface->setspecs[genid]; ipatch_item_prop_notify(IPATCH_ITEM(item), pspec, ipatch_util_value_bool_true, ipatch_util_value_bool_false); } if(coarsevalchanged) /* do the property change notify if it actually changed */ { pspec = iface->specs[coarse]; ipatch_sf2_gen_amount_to_value(genid, &newcoarseamt, &newval); ipatch_sf2_gen_amount_to_value(genid, &oldcoarseamt, &oldval); ipatch_item_prop_notify(IPATCH_ITEM(item), pspec, &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } if(oldcoarseset != newcoarseset) /* "set" state of property changed? */ { pspec = iface->setspecs[coarse]; ipatch_item_prop_notify(IPATCH_ITEM(item), pspec, IPATCH_UTIL_VALUE_BOOL(newcoarseset), IPATCH_UTIL_VALUE_BOOL(oldcoarseset)); } } else { if(gen_info->unit == IPATCH_UNIT_TYPE_RANGE) /* range property? */ { range = ipatch_value_get_range(value); amt.range.low = range->low; amt.range.high = range->high; } else { val = g_value_get_int(value); amt.sword = val; } IPATCH_ITEM_WLOCK(item); /* prop notify done by IpatchItem methods, so just set value */ genarray->values[genid] = amt; /* get old value of set flag, and then set it (if no already) */ oldset = IPATCH_SF2_GEN_ARRAY_TEST_FLAG(genarray, genid); IPATCH_SF2_GEN_ARRAY_SET_FLAG(genarray, genid); /* value is set */ IPATCH_ITEM_WUNLOCK(item); if(oldset != TRUE) /* "set" state of property changed? */ { pspec = iface->setspecs[genid]; ipatch_item_prop_notify(IPATCH_ITEM(item), pspec, ipatch_util_value_bool_true, ipatch_util_value_bool_false); } } return (TRUE); } /** * ipatch_sf2_gen_item_iface_get_property: (skip) * @item: IpatchItem instance with generators * @property_id: Property id to set * @value: Value to set property to * * Used internally for classes with generator properties, to get values thereof. * * Returns: %TRUE if @property_id handled, %FALSE otherwise */ gboolean ipatch_sf2_gen_item_iface_get_property(IpatchSF2GenItem *item, guint property_id, GValue *value) { IpatchSF2GenItemIface *iface; IpatchSF2GenArray *genarray; const IpatchSF2GenInfo *gen_info; IpatchSF2GenAmount amt; IpatchRange range; int genid, coarse, val; gboolean setflag; iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item); /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */ g_return_val_if_fail(iface->genarray_ofs != 0, FALSE); genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs); /* a "-set" property? */ if(property_id >= IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID && property_id < IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID + IPATCH_SF2_GEN_COUNT) { genid = property_id - IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID; /* generator valid for zone type? */ if(!ipatch_sf2_gen_is_valid(genid, iface->propstype)) { return (FALSE); } IPATCH_ITEM_RLOCK(item); setflag = IPATCH_SF2_GEN_ARRAY_TEST_FLAG(genarray, genid); IPATCH_ITEM_RUNLOCK(item); g_value_set_boolean(value, setflag); return (TRUE); } /* regular generator property? */ if(property_id < IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID || property_id >= IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID + IPATCH_SF2_GEN_COUNT) { return (FALSE); } genid = property_id - IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID; /* generator valid for propstype? */ if(!ipatch_sf2_gen_is_valid(genid, iface->propstype)) { return (FALSE); } gen_info = &ipatch_sf2_gen_info [genid]; if(gen_info->unit == IPATCH_UNIT_TYPE_RANGE) /* range property? */ { IPATCH_ITEM_WLOCK(item); /* OPTME - lock might not be necessary? */ amt = genarray->values[genid]; IPATCH_ITEM_WUNLOCK(item); range.low = amt.range.low; range.high = amt.range.high; ipatch_value_set_range(value, &range); } else if(gen_info->unit == IPATCH_UNIT_TYPE_SAMPLES) { /* get 2 generators - fine and coarse (32k) sample values */ if(genid == IPATCH_SF2_GEN_SAMPLE_START) { coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_START; } else if(genid == IPATCH_SF2_GEN_SAMPLE_END) { coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_END; } else if(genid == IPATCH_SF2_GEN_SAMPLE_LOOP_START) { coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START; } else if(genid == IPATCH_SF2_GEN_SAMPLE_LOOP_END) { coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END; } else { g_return_val_if_fail(NOT_REACHED, FALSE); } IPATCH_ITEM_WLOCK(item); /* atomically get both gens */ val = genarray->values[genid].uword; val |= genarray->values[coarse].uword << 15; IPATCH_ITEM_WUNLOCK(item); g_value_set_int(value, val); } /* sword read is atomic */ else { g_value_set_int(value, genarray->values[genid].sword); } return (TRUE); } libinstpatch-1.1.6/libinstpatch/IpatchSF2GenItem.h000066400000000000000000000105171400263525300220400ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_GEN_ITEM_H__ #define __IPATCH_SF2_GEN_ITEM_H__ #include #include #include /* for generator properties (zones, instruments, and presets) */ /** * IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID: (skip) */ #define IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID 1 /* first gen prop */ /** * IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID: (skip) */ #define IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID 80 /* first gen-set prop */ /** * IPATCH_SF2_GEN_ITEM_FIRST_PROP_USER_ID: (skip) */ #define IPATCH_SF2_GEN_ITEM_FIRST_PROP_USER_ID 160 /* first ID usable for other properties */ /* forward type declarations */ typedef struct _IpatchSF2GenItem IpatchSF2GenItem; // dummy typedef typedef struct _IpatchSF2GenItemIface IpatchSF2GenItemIface; #define IPATCH_TYPE_SF2_GEN_ITEM (ipatch_sf2_gen_item_get_type ()) #define IPATCH_SF2_GEN_ITEM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SF2_GEN_ITEM, \ IpatchSF2GenItem)) #define IPATCH_SF2_GEN_ITEM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SF2_GEN_ITEM, \ IpatchSF2GenItemIface)) #define IPATCH_IS_SF2_GEN_ITEM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SF2_GEN_ITEM)) #define IPATCH_SF2_GEN_ITEM_GET_IFACE(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IPATCH_TYPE_SF2_GEN_ITEM, \ IpatchSF2GenItemIface)) /* generator item interface */ struct _IpatchSF2GenItemIface { GTypeInterface parent_iface; /*< public >*/ IpatchSF2GenPropsType propstype; /* gen properties type for this class */ guint genarray_ofs; /* offset in item instance to generator array pointer */ GParamSpec *specs[IPATCH_SF2_GEN_COUNT]; /* genid -> prop pspec array */ GParamSpec *setspecs[IPATCH_SF2_GEN_COUNT]; /* genid -> "-set" prop pspec array */ }; GType ipatch_sf2_gen_item_get_type(void); gboolean ipatch_sf2_gen_item_get_amount(IpatchSF2GenItem *item, guint genid, IpatchSF2GenAmount *out_amt); void ipatch_sf2_gen_item_set_amount(IpatchSF2GenItem *item, guint genid, IpatchSF2GenAmount *amt); void ipatch_sf2_gen_item_set_gen_flag(IpatchSF2GenItem *item, guint genid, gboolean setflag); guint ipatch_sf2_gen_item_count_set(IpatchSF2GenItem *item); void ipatch_sf2_gen_item_copy_all(IpatchSF2GenItem *item, IpatchSF2GenArray *array); void ipatch_sf2_gen_item_copy_set(IpatchSF2GenItem *item, IpatchSF2GenArray *array); void ipatch_sf2_gen_item_set_note_range(IpatchSF2GenItem *item, int low, int high); void ipatch_sf2_gen_item_set_velocity_range(IpatchSF2GenItem *item, int low, int high); gboolean ipatch_sf2_gen_item_in_range(IpatchSF2GenItem *item, int note, int velocity); gboolean ipatch_sf2_gen_item_intersect_test(IpatchSF2GenItem *item, const IpatchSF2GenArray *genarray); GParamSpec *ipatch_sf2_gen_item_class_get_pspec(GObjectClass *klass, guint genid); GParamSpec *ipatch_sf2_gen_item_class_get_pspec_set(GObjectClass *klass, guint genid); void ipatch_sf2_gen_item_iface_install_properties(GObjectClass *klass, IpatchSF2GenPropsType propstype, GParamSpec ***specs, GParamSpec ***setspecs); gboolean ipatch_sf2_gen_item_iface_set_property(IpatchSF2GenItem *item, guint property_id, const GValue *value); gboolean ipatch_sf2_gen_item_iface_get_property(IpatchSF2GenItem *item, guint property_id, GValue *value); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2Gen_tables.c000066400000000000000000000212541400263525300225460ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /* * IpatchSF2Gen_tables.c - SoundFont generator table structures */ #include #include "IpatchSF2Gen.h" #include "IpatchUnit.h" #include "ipatch_priv.h" /* Default range value stored in 2 byte form with correct host byte order */ #define DEFRANGE (GUINT16_FROM_LE (0x7F00)) #define MAXNEG -32768 #define MAXPOS 32767 #define UMAX 65535 /* generator info */ const IpatchSF2GenInfo ipatch_sf2_gen_info[] = { /* StartAddrOfs */ { {0}, {MAXPOS}, {0}, IPATCH_UNIT_TYPE_SAMPLES, N_("Sample Start Ofs"), NULL}, /* EndAddrOfs */ { {MAXNEG}, {0}, {0}, IPATCH_UNIT_TYPE_SAMPLES, N_("Sample End Ofs"), NULL}, /* StartLoopAddrOfs */ { {MAXNEG}, {MAXPOS}, {0}, IPATCH_UNIT_TYPE_SAMPLES, N_("Sample Loop Start Ofs"), NULL }, /* EndLoopAddrOfs */ { {MAXNEG}, {MAXPOS}, {0}, IPATCH_UNIT_TYPE_SAMPLES, N_("Sample Loop End Ofs"), NULL }, /* StartAddrCoarseOfs */ { {0}, {MAXPOS}, {0}, IPATCH_UNIT_TYPE_32K_SAMPLES, N_("Sample Start Coarse Ofs"), NULL }, /* ModLFO2Pitch */ { {-12000}, {12000}, {0}, IPATCH_UNIT_TYPE_CENTS, N_("To Pitch"), N_("Modulation oscillator to pitch") }, /* VibLFO2Pitch */ { {-12000}, {12000}, {0}, IPATCH_UNIT_TYPE_CENTS, N_("To Pitch"), N_("Vibrato oscillator to pitch") }, /* ModEnv2Pitch */ { {-12000}, {12000}, {0}, IPATCH_UNIT_TYPE_CENTS, N_("To Pitch"), N_("Modulation envelope to pitch") }, /* FilterFc */ { {1500}, {13500}, {13500}, IPATCH_UNIT_TYPE_SF2_ABS_PITCH, N_("Filter cutoff"), N_("Low pass filter cutoff frequency") }, /* FilterQ */ { {0}, {960}, {0}, IPATCH_UNIT_TYPE_CENTIBELS, N_("Filter Q"), N_("Low pass filter Q factor") }, /* ModLFO2FilterFc */ { {-12000}, {12000}, {0}, IPATCH_UNIT_TYPE_CENTS, N_("To Filter Cutoff"), N_("Modulation oscillator to filter cutoff") }, /* ModEnv2FilterFc */ { {-12000}, {12000}, {0}, IPATCH_UNIT_TYPE_CENTS, N_("To Filter Cutoff"), N_("Modulation envelope to filter cutoff") }, /* EndAddrCoarseOfs */ { {MAXNEG}, {0}, {0}, IPATCH_UNIT_TYPE_32K_SAMPLES, N_("Sample End Coarse Ofs"), NULL }, /* ModLFO2Vol */ { {-960}, {960}, {0}, IPATCH_UNIT_TYPE_CENTIBELS, N_("To Volume"), N_("Modulation oscillator to volume") }, /* Unused1 */ { {0}, {0}, {0}, IPATCH_UNIT_TYPE_NONE, NULL, NULL}, /* ChorusSend */ { {0}, {1000}, {0}, IPATCH_UNIT_TYPE_TENTH_PERCENT, N_("Chorus"), NULL}, /* ReverbSend */ { {0}, {1000}, {0}, IPATCH_UNIT_TYPE_TENTH_PERCENT, N_("Reverb"), NULL}, /* Pan */ { {-500}, {500}, {0}, IPATCH_UNIT_TYPE_TENTH_PERCENT, N_("Pan"), N_("Panning") }, /* Unused2 */ { {0}, {0}, {0}, IPATCH_UNIT_TYPE_NONE, NULL, NULL}, /* Unused3 */ { {0}, {0}, {0}, IPATCH_UNIT_TYPE_NONE, NULL, NULL}, /* Unused4 */ { {0}, {0}, {0}, IPATCH_UNIT_TYPE_NONE, NULL, NULL}, /* ModLFODelay */ { {-12000}, {5000}, {-12000}, IPATCH_UNIT_TYPE_SF2_ABS_TIME, N_("Delay"), N_("Modulation oscillator delay") }, /* ModLFOFreq */ { {-16000}, {4500}, {0}, IPATCH_UNIT_TYPE_SF2_ABS_PITCH, N_("Frequency"), N_("Modulation oscillator frequency") }, /* VibLFODelay */ { {-12000}, {5000}, {-12000}, IPATCH_UNIT_TYPE_SF2_ABS_TIME, N_("Delay"), N_("Vibrato oscillator delay") }, /* VibLFOFreq */ { {-16000}, {4500}, {0}, IPATCH_UNIT_TYPE_SF2_ABS_PITCH, N_("Frequency"), N_("Vibrato oscillator frequency") }, /* ModEnvDelay */ { {-12000}, {5000}, {-12000}, IPATCH_UNIT_TYPE_SF2_ABS_TIME, N_("Delay"), N_("Modulation envelope delay") }, /* ModEnvAttack */ { {-12000}, {8000}, {-12000}, IPATCH_UNIT_TYPE_SF2_ABS_TIME, N_("Attack"), N_("Modulation envelope attack") }, /* ModEnvHold */ { {-12000}, {5000}, {-12000}, IPATCH_UNIT_TYPE_SF2_ABS_TIME, N_("Hold"), N_("Modulation envelope hold") }, /* ModEnvDecay */ { {-12000}, {8000}, {-12000}, IPATCH_UNIT_TYPE_SF2_ABS_TIME, N_("Decay"), N_("Modulation envelope decay") }, /* ModEnvSustain */ { {0}, {1000}, {0}, IPATCH_UNIT_TYPE_TENTH_PERCENT, N_("Sustain"), N_("Modulation envelope sustain") }, /* ModEnvRelease */ { {-12000}, {8000}, {-12000}, IPATCH_UNIT_TYPE_SF2_ABS_TIME, N_("Release"), N_("Modulation envelope release") }, /* Note2ModEnvHold */ { {-1200}, {1200}, {0}, IPATCH_UNIT_TYPE_CENTS, N_("Note to Hold"), N_("MIDI note to modulation envelope hold") }, /* Note2ModEnvDecay */ { {-1200}, {1200}, {0}, IPATCH_UNIT_TYPE_CENTS, N_("Note to Decay"), N_("MIDI note to modulation envelope decay") }, /* VolEnvDelay */ { {-12000}, {5000}, {-12000}, IPATCH_UNIT_TYPE_SF2_ABS_TIME, N_("Delay"), N_("Volume envelope delay") }, /* VolEnvAttack */ { {-12000}, {8000}, {-12000}, IPATCH_UNIT_TYPE_SF2_ABS_TIME, N_("Attack"), N_("Volume envelope attack") }, /* VolEnvHold */ { {-12000}, {5000}, {-12000}, IPATCH_UNIT_TYPE_SF2_ABS_TIME, N_("Hold"), N_("Volume envelope hold") }, /* VolEnvDecay */ { {-12000}, {8000}, {-12000}, IPATCH_UNIT_TYPE_SF2_ABS_TIME, N_("Decay"), N_("Volume envelope decay") }, /* VolEnvSustain */ { {0}, {1440}, {0}, IPATCH_UNIT_TYPE_CENTIBELS, N_("Sustain"), N_("Volume envelope sustain") }, /* VolEnvRelease */ { {-12000}, {8000}, {-12000}, IPATCH_UNIT_TYPE_SF2_ABS_TIME, N_("Release"), N_("Volume envelope release") }, /* Note2VolEnvHold */ { {-1200}, {1200}, {0}, IPATCH_UNIT_TYPE_CENTS, N_("Note to Hold"), N_("MIDI note to volume envelope hold") }, /* Note2VolEnvDecay */ { {-1200}, {1200}, {0}, IPATCH_UNIT_TYPE_CENTS, N_("Note to Decay"), N_("MIDI note to volume envelope decay") }, /* Instrument */ { {0}, {UMAX}, {0}, IPATCH_UNIT_TYPE_UINT, N_("Instrument ID"), NULL}, /* Reserved1 */ { {0}, {0}, {0}, IPATCH_UNIT_TYPE_NONE, NULL, NULL}, /* NoteRange */ { {0}, {127}, {DEFRANGE}, IPATCH_UNIT_TYPE_RANGE, N_("Note Range"), NULL}, /* VelRange */ { {0}, {127}, {DEFRANGE}, IPATCH_UNIT_TYPE_RANGE, N_("Velocity Range"), NULL }, /* StartLoopAddrCoarseOfs */ { {MAXNEG}, {MAXPOS}, {0}, IPATCH_UNIT_TYPE_32K_SAMPLES, N_("Sample Loop Start Coarse Ofs"), NULL }, /* FixedNote */ { {-1}, {127}, {-1}, IPATCH_UNIT_TYPE_INT, N_("Fixed Note"), NULL}, /* Velocity */ { {-1}, {127}, {-1}, IPATCH_UNIT_TYPE_INT, N_("Fixed Velocity"), NULL}, /* InitAttenuation */ { {0}, {1440}, {0}, IPATCH_UNIT_TYPE_CENTIBELS, N_("Attenuation"), N_("Volume attenuation") }, /* Reserved2 */ { {0}, {0}, {0}, IPATCH_UNIT_TYPE_NONE, NULL, NULL}, /* EndLoopAddrCoarseOfs */ { {MAXNEG}, {MAXPOS}, {0}, IPATCH_UNIT_TYPE_32K_SAMPLES, N_("Sample Loop End Coarse Ofs"), NULL }, /* CourseTune */ { {-120}, {120}, {0}, IPATCH_UNIT_TYPE_SEMITONES, N_("Coarse Tune"), NULL}, /* FineTune */ { {-99}, {99}, {0}, IPATCH_UNIT_TYPE_CENTS, N_("Fine Tune"), NULL}, /* sampleId */ { {0}, {UMAX}, {0}, IPATCH_UNIT_TYPE_UINT, N_("Sample ID"), NULL}, /* SampleModes */ { {0}, {3}, {0}, IPATCH_UNIT_TYPE_UINT, N_("Sample Modes"), NULL}, /* Reserved3 */ { {0}, {0}, {0}, IPATCH_UNIT_TYPE_NONE, NULL, NULL}, /* ScaleTuning */ { {0}, {1200}, {100}, IPATCH_UNIT_TYPE_CENTS, N_("Scale Tune"), NULL}, /* ExclusiveClass */ { {0}, {127}, {0}, IPATCH_UNIT_TYPE_INT, N_("Exclusive Class"), NULL}, /* RootNote */ { {-1}, {127}, {-1}, IPATCH_UNIT_TYPE_INT, N_("Root Note"), NULL} }; /* Getter function returning ipatch_sf2_gen_info table. Useful when libinstpatch library is used as a shared library linked at load time. */ const IpatchSF2GenInfo * ipatch_sf2_get_gen_info(void) { return ipatch_sf2_gen_info; } libinstpatch-1.1.6/libinstpatch/IpatchSF2IZone.c000066400000000000000000000653461400263525300215410ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2IZone * @short_description: SoundFont instrument zone object * @see_also: #IpatchSF2Inst, #IpatchSF2Sample * @stability: Stable * * Instrument zones are children to #IpatchSF2Inst objects and define how * their referenced #IpatchSF2Sample is synthesized. */ #include #include #include #include #include "IpatchSF2IZone.h" #include "IpatchSF2GenItem.h" #include "IpatchSample.h" #include "IpatchContainer.h" #include "IpatchTypeProp.h" #include "ipatch_priv.h" enum { /* generator IDs are used for lower numbers */ PROP_LINK_ITEM = IPATCH_SF2_GEN_ITEM_FIRST_PROP_USER_ID, PROP_SAMPLE_SIZE, PROP_SAMPLE_FORMAT, PROP_SAMPLE_RATE, PROP_SAMPLE_DATA, PROP_LOOP_TYPE, PROP_LOOP_START, PROP_LOOP_END, PROP_ROOT_NOTE, PROP_FINE_TUNE }; static void ipatch_sf2_izone_sample_iface_init(IpatchSampleIface *sample_iface); static gboolean ipatch_sf2_izone_sample_iface_open(IpatchSampleHandle *handle, GError **err); static void ipatch_sf2_izone_class_init(IpatchSF2IZoneClass *klass); static void ipatch_sf2_izone_gen_item_iface_init (IpatchSF2GenItemIface *genitem_iface); static void ipatch_sf2_izone_init(IpatchSF2IZone *izone); static inline void ipatch_sf2_izone_get_root_note(IpatchSF2IZone *izone, GValue *value); static inline void ipatch_sf2_izone_get_fine_tune(IpatchSF2IZone *izone, GValue *value); static void ipatch_sf2_izone_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sf2_izone_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); /* For quicker access without lookup */ static GParamSpec *root_note_pspec; static GParamSpec *fine_tune_pspec; /* For passing data from class init to gen item interface init */ static GParamSpec **gen_item_specs = NULL; static GParamSpec **gen_item_setspecs = NULL; G_DEFINE_TYPE_WITH_CODE(IpatchSF2IZone, ipatch_sf2_izone, IPATCH_TYPE_SF2_ZONE, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_sf2_izone_sample_iface_init) G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SF2_GEN_ITEM, ipatch_sf2_izone_gen_item_iface_init)) /* sample interface initialization */ static void ipatch_sf2_izone_sample_iface_init(IpatchSampleIface *sample_iface) { sample_iface->open = ipatch_sf2_izone_sample_iface_open; sample_iface->loop_types = ipatch_sample_loop_types_standard_release; } static gboolean ipatch_sf2_izone_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchSF2Zone *zone = IPATCH_SF2_ZONE(handle->sample); IpatchItem *link_item; gboolean retval; link_item = ipatch_sf2_zone_get_link_item(zone); /* ++ ref link_item */ g_return_val_if_fail(link_item != NULL, FALSE); retval = ipatch_sample_handle_cascade_open(handle, IPATCH_SAMPLE(link_item), err); g_object_unref(link_item); /* -- unref link_item */ return (retval); } /* gen item interface initialization */ static void ipatch_sf2_izone_gen_item_iface_init(IpatchSF2GenItemIface *genitem_iface) { genitem_iface->genarray_ofs = G_STRUCT_OFFSET(IpatchSF2Zone, genarray); genitem_iface->propstype = IPATCH_SF2_GEN_PROPS_INST; g_return_if_fail(gen_item_specs != NULL); g_return_if_fail(gen_item_setspecs != NULL); memcpy(&genitem_iface->specs, gen_item_specs, sizeof(genitem_iface->specs)); memcpy(&genitem_iface->setspecs, gen_item_setspecs, sizeof(genitem_iface->setspecs)); g_free(gen_item_specs); g_free(gen_item_setspecs); } static void ipatch_sf2_izone_class_init(IpatchSF2IZoneClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->get_property = ipatch_sf2_izone_get_property; item_class->item_set_property = ipatch_sf2_izone_set_property; g_object_class_install_property(obj_class, PROP_LINK_ITEM, g_param_spec_object("link-item", _("Link item"), _("Link item"), IPATCH_TYPE_SF2_SAMPLE, G_PARAM_READWRITE)); /* properties defined by IpatchSample interface */ ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_SIZE, "sample-size"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_FORMAT, "sample-format"); ipatch_sample_install_property(obj_class, PROP_SAMPLE_RATE, "sample-rate"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_DATA, "sample-data"); ipatch_sample_install_property(obj_class, PROP_LOOP_TYPE, "loop-type"); ipatch_sample_install_property(obj_class, PROP_LOOP_START, "loop-start"); ipatch_sample_install_property(obj_class, PROP_LOOP_END, "loop-end"); root_note_pspec = ipatch_sample_install_property(obj_class, PROP_ROOT_NOTE, "root-note"); fine_tune_pspec = ipatch_sample_install_property(obj_class, PROP_FINE_TUNE, "fine-tune"); /* install generator properties */ ipatch_sf2_gen_item_iface_install_properties(obj_class, IPATCH_SF2_GEN_PROPS_INST, &gen_item_specs, &gen_item_setspecs); } static inline void ipatch_sf2_izone_get_root_note(IpatchSF2IZone *izone, GValue *value) { IpatchSF2GenAmount amt; IpatchSF2Sample *sample; int val = 0; /* root note override not set or -1? - Get sample root note value. */ if(!ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(izone), IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE, &amt) || amt.sword == -1) { /* root note override not set, get from sample */ sample = ipatch_sf2_izone_get_sample(izone); /* ++ ref sample */ if(sample) { g_object_get(sample, "root-note", &val, NULL); g_object_unref(sample); /* -- unref sample */ } } else { val = amt.uword; } g_value_set_int(value, val); } static inline void ipatch_sf2_izone_get_fine_tune(IpatchSF2IZone *izone, GValue *value) { IpatchSF2GenAmount amt; IpatchSF2Sample *sample; int val = 0; /* fine tune override set? */ if(!ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(izone), IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE, &amt)) { /* fine tune override not set, get from sample */ sample = ipatch_sf2_izone_get_sample(izone); /* ++ ref sample */ if(sample) { g_object_get(sample, "fine-tune", &val, NULL); g_object_unref(sample); /* -- unref sample */ } } else { val = amt.sword; } g_value_set_int(value, val); } static void ipatch_sf2_izone_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSF2IZone *izone = IPATCH_SF2_IZONE(object); IpatchSF2Sample *sample; IpatchSF2GenAmount amt; GValue vals[2]; /* Gets zeroed below */ guint genid; guint uval; int val = 0; /* "root-note" and "fine-tune" sample properties get updated for IZone * override property or -set property */ if(property_id >= IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID) { genid = property_id - IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID; } else { genid = property_id - IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID; } if(genid == IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE) { memset(vals, 0, sizeof(vals)); g_value_init(&vals[0], G_TYPE_INT); ipatch_sf2_izone_get_root_note(izone, &vals[0]); } else if(genid == IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE) { memset(vals, 0, sizeof(vals)); g_value_init(&vals[0], G_TYPE_INT); ipatch_sf2_izone_get_fine_tune(izone, &vals[0]); } if(ipatch_sf2_gen_item_iface_set_property((IpatchSF2GenItem *)object, property_id, value)) { if(genid == IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE) { g_value_init(&vals[1], G_TYPE_INT); ipatch_sf2_izone_get_root_note(izone, &vals[1]); ipatch_item_prop_notify((IpatchItem *)object, root_note_pspec, &vals[1], &vals[0]); } else if(genid == IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE) { g_value_init(&vals[1], G_TYPE_INT); ipatch_sf2_izone_get_fine_tune(izone, &vals[1]); ipatch_item_prop_notify((IpatchItem *)object, fine_tune_pspec, &vals[1], &vals[0]); } } else { switch(property_id) { case PROP_LINK_ITEM: sample = g_value_get_object(value); g_return_if_fail(IPATCH_IS_SF2_SAMPLE(sample)); ipatch_sf2_zone_set_link_item_no_notify((IpatchSF2Zone *)izone, (IpatchItem *)sample, NULL); break; case PROP_SAMPLE_RATE: sample = ipatch_sf2_izone_get_sample(izone); /* ++ ref sample */ if(sample) { g_object_set(sample, "sample-rate", g_value_get_int(value), NULL); g_object_unref(sample); /* -- unref sample */ } break; case PROP_LOOP_TYPE: val = g_value_get_enum(value); if(val == IPATCH_SAMPLE_LOOP_NONE) { amt.uword = IPATCH_SF2_GEN_SAMPLE_MODE_NOLOOP; } else if(val == IPATCH_SAMPLE_LOOP_RELEASE) { amt.uword = IPATCH_SF2_GEN_SAMPLE_MODE_LOOP_RELEASE; } else { amt.uword = IPATCH_SF2_GEN_SAMPLE_MODE_LOOP; } ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(izone), IPATCH_SF2_GEN_SAMPLE_MODES, &amt); break; case PROP_LOOP_START: sample = ipatch_sf2_izone_get_sample(izone); /* ++ ref sample */ if(sample) { g_object_get(sample, "loop-start", &uval, NULL); val = g_value_get_uint(value) - uval; /* loop start offset */ g_object_unref(sample); /* -- unref sample */ if(val >= 0) { amt.sword = val >> 15; } else { amt.sword = -(-val >> 15); } ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(izone), IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START, &amt); if(val >= 0) { amt.sword = val & 0x7FFF; } else { amt.sword = -(-val & 0x7FFF); } ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(izone), IPATCH_SF2_GEN_SAMPLE_LOOP_START, &amt); } break; case PROP_LOOP_END: sample = ipatch_sf2_izone_get_sample(izone); /* ++ ref sample */ if(sample) { g_object_get(sample, "loop-end", &uval, NULL); val = g_value_get_uint(value) - uval; /* loop end offset */ g_object_unref(sample); /* -- unref sample */ if(val >= 0) { amt.sword = val >> 15; } else { amt.sword = -(-val >> 15); } ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(izone), IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END, &amt); if(val >= 0) { amt.sword = val & 0x7FFF; } else { amt.sword = -(-val & 0x7FFF); } ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(izone), IPATCH_SF2_GEN_SAMPLE_LOOP_END, &amt); } break; case PROP_ROOT_NOTE: amt.uword = g_value_get_int(value); ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(izone), IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE, &amt); break; case PROP_FINE_TUNE: amt.sword = g_value_get_int(value); ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(izone), IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE, &amt); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } } static void ipatch_sf2_izone_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSF2IZone *izone = IPATCH_SF2_IZONE(object); IpatchSF2Sample *sample; IpatchSF2GenAmount amt; guint uval = 0; int val = 0; if(!ipatch_sf2_gen_item_iface_get_property((IpatchSF2GenItem *)object, property_id, value)) { switch(property_id) { case PROP_LINK_ITEM: g_value_take_object(value, ipatch_sf2_zone_get_link_item ((IpatchSF2Zone *)izone)); break; case PROP_SAMPLE_SIZE: sample = ipatch_sf2_izone_get_sample(izone); /* ++ ref sample */ if(sample) { g_object_get_property((GObject *)sample, "sample-size", value); g_object_unref(sample); /* -- unref sample */ } break; case PROP_SAMPLE_FORMAT: sample = ipatch_sf2_izone_get_sample(izone); /* ++ ref sample */ if(sample) { g_object_get_property((GObject *)sample, "sample-format", value); g_object_unref(sample); /* -- unref sample */ } break; case PROP_SAMPLE_RATE: sample = ipatch_sf2_izone_get_sample(izone); /* ++ ref sample */ if(sample) { g_object_get_property((GObject *)sample, "sample-rate", value); g_object_unref(sample); /* -- unref sample */ } break; case PROP_SAMPLE_DATA: sample = ipatch_sf2_izone_get_sample(izone); /* ++ ref sample */ if(sample) { g_object_get_property((GObject *)sample, "sample-data", value); g_object_unref(sample); /* -- unref sample */ } break; case PROP_LOOP_TYPE: ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(izone), IPATCH_SF2_GEN_SAMPLE_MODES, &amt); if(amt.uword == IPATCH_SF2_GEN_SAMPLE_MODE_NOLOOP) { val = IPATCH_SAMPLE_LOOP_NONE; } /* not used. Should be interpreted as "no loop" */ else if(amt.uword == IPATCH_SF2_GEN_SAMPLE_MODE_UNUSED) { val = IPATCH_SAMPLE_LOOP_NONE; } else if(amt.uword == IPATCH_SF2_GEN_SAMPLE_MODE_LOOP_RELEASE) { val = IPATCH_SAMPLE_LOOP_RELEASE; } else { val = IPATCH_SAMPLE_LOOP_STANDARD; } g_value_set_enum(value, val); break; case PROP_LOOP_START: sample = ipatch_sf2_izone_get_sample(izone); /* ++ ref sample */ if(sample) { g_object_get(sample, "loop-start", &uval, NULL); g_object_unref(sample); /* -- unref sample */ val = uval; } ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(izone), IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START, &amt); val += (int)amt.sword << 15; ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(izone), IPATCH_SF2_GEN_SAMPLE_LOOP_START, &amt); val += amt.sword; g_value_set_uint(value, CLAMP(val, 0, G_MAXINT)); break; case PROP_LOOP_END: sample = ipatch_sf2_izone_get_sample(izone); /* ++ ref sample */ if(sample) { g_object_get(sample, "loop-end", &uval, NULL); g_object_unref(sample); /* -- unref sample */ val = uval; } ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(izone), IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END, &amt); val += (int)amt.sword << 15; ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(izone), IPATCH_SF2_GEN_SAMPLE_LOOP_END, &amt); val += amt.sword; g_value_set_uint(value, CLAMP(val, 0, G_MAXINT)); break; case PROP_ROOT_NOTE: ipatch_sf2_izone_get_root_note(izone, value); break; case PROP_FINE_TUNE: ipatch_sf2_izone_get_fine_tune(izone, value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } } static void ipatch_sf2_izone_init(IpatchSF2IZone *izone) { ipatch_sf2_gen_array_init(&((IpatchSF2Zone *)izone)->genarray, FALSE, FALSE); } /** * ipatch_sf2_izone_new: * * Create a new SoundFont instrument zone object. * * Returns: New SoundFont instrument zone with a reference count of 1. Caller * owns the reference and removing it will destroy the item, unless another * reference is added (if its parented for example). */ IpatchSF2IZone * ipatch_sf2_izone_new(void) { return (IPATCH_SF2_IZONE(g_object_new(IPATCH_TYPE_SF2_IZONE, NULL))); } /** * ipatch_sf2_izone_first: (skip) * @iter: Patch item iterator containing #IpatchSF2IZone items * * Gets the first item in an instrument zone iterator. A convenience * wrapper for ipatch_iter_first(). * * Returns: The first instrument zone in @iter or %NULL if empty. */ IpatchSF2IZone * ipatch_sf2_izone_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_SF2_IZONE(obj)); } else { return (NULL); } } /** * ipatch_sf2_izone_next: (skip) * @iter: Patch item iterator containing #IpatchSF2IZone items * * Gets the next item in an instrument zone iterator. A convenience wrapper * for ipatch_iter_next(). * * Returns: The next instrument zone in @iter or %NULL if at the end of * the list. */ IpatchSF2IZone * ipatch_sf2_izone_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_SF2_IZONE(obj)); } else { return (NULL); } } /** * ipatch_sf2_izone_set_sample: * @izone: Instrument zone to set referenced sample of * @sample: Sample to set instrument zone's referenced item to * * Sets the referenced sample of an instrument zone. */ void ipatch_sf2_izone_set_sample(IpatchSF2IZone *izone, IpatchSF2Sample *sample) { g_return_if_fail(IPATCH_IS_SF2_IZONE(izone)); g_return_if_fail(IPATCH_IS_SF2_SAMPLE(sample)); ipatch_sf2_zone_set_link_item(IPATCH_SF2_ZONE(izone), IPATCH_ITEM(sample)); } /** * ipatch_sf2_izone_get_sample: * @izone: Instrument zone to get referenced sample from * * Gets the referenced sample from an instrument zone. * The returned sample's reference count is incremented and the caller * is responsible for unrefing it with g_object_unref(). * * Returns: (transfer full): Instrument zone's referenced sample or %NULL if global * zone. Remember to unreference the sample with g_object_unref() when * done with it. */ IpatchSF2Sample * ipatch_sf2_izone_get_sample(IpatchSF2IZone *izone) { IpatchItem *item; g_return_val_if_fail(IPATCH_IS_SF2_IZONE(izone), NULL); item = ipatch_sf2_zone_get_link_item(IPATCH_SF2_ZONE(izone)); return (item ? IPATCH_SF2_SAMPLE(item) : NULL); } /** * ipatch_sf2_izone_get_stereo_link: * @izone: Instrument zone * * Get the stereo linked instrument zone of another zone. This is a zone which * has the same #IpatchSF2Inst parent and has its link-item set to the counter * part of @izone. * * Returns: (transfer full): Stereo linked instrument zone or %NULL if not stereo or it could not * be found in the same instrument. Caller owns a reference to the returned * object. */ /* FIXME - This function is kind of a hack, until stereo IpatchSF2Sample and * IpatchSF2IZones are implemented */ IpatchSF2IZone * ipatch_sf2_izone_get_stereo_link(IpatchSF2IZone *izone) { IpatchSF2IZone *linked_izone = NULL; IpatchSF2Sample *sample = NULL, *linked_sample = NULL; IpatchItem *parent = NULL; IpatchList *children = NULL; IpatchSF2GenAmount z_noterange, cmp_noterange, z_velrange, cmp_velrange; int channel; GList *p; g_return_val_if_fail(IPATCH_IS_SF2_IZONE(izone), NULL); sample = ipatch_sf2_izone_get_sample(izone); /* ++ ref sample */ if(!sample) { return (NULL); } g_object_get(sample, "channel", &channel, "linked-sample", &linked_sample, /* ++ ref linked sample */ NULL); if(channel == IPATCH_SF2_SAMPLE_CHANNEL_MONO || !linked_sample) { goto ret; } parent = ipatch_item_get_parent((IpatchItem *)izone); /* ++ ref parent */ if(!IPATCH_IS_CONTAINER(parent)) { goto ret; } /* ++ ref children */ if(!(children = ipatch_container_get_children((IpatchContainer *)parent, IPATCH_TYPE_SF2_IZONE))) { goto ret; } /* Check likely previous and next zone of izone for performance */ p = g_list_find(children->items, izone); if(p->prev && ipatch_sf2_zone_peek_link_item(p->prev->data) == (IpatchItem *)linked_sample) { linked_izone = g_object_ref(p->prev->data); } if(p->next && ipatch_sf2_zone_peek_link_item(p->next->data) == (IpatchItem *)linked_sample) { if(!linked_izone) { linked_izone = g_object_ref(p->next->data); goto ret; } /* prev is also a match, this can happen in instruments with multiple pairs * of the same stereo sample - Return zone with intersecting note/velocity * ranges or fall through to exhaustive search. */ ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)izone, IPATCH_SF2_GEN_NOTE_RANGE, &z_noterange); ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)izone, IPATCH_SF2_GEN_VELOCITY_RANGE, &z_velrange); ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)linked_izone, IPATCH_SF2_GEN_NOTE_RANGE, &cmp_noterange); ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)linked_izone, IPATCH_SF2_GEN_VELOCITY_RANGE, &cmp_velrange); if(ipatch_sf2_gen_range_intersect_test(&z_noterange, &cmp_noterange) && ipatch_sf2_gen_range_intersect_test(&z_velrange, &cmp_velrange)) { goto ret; } g_object_unref(linked_izone); /* -- unref linked izone */ linked_izone = NULL; ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)(p->next->data), IPATCH_SF2_GEN_NOTE_RANGE, &cmp_noterange); ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)(p->next->data), IPATCH_SF2_GEN_VELOCITY_RANGE, &cmp_velrange); if(ipatch_sf2_gen_range_intersect_test(&z_noterange, &cmp_noterange) && ipatch_sf2_gen_range_intersect_test(&z_velrange, &cmp_velrange)) { linked_izone = g_object_ref(p->next->data); goto ret; } } else { if(linked_izone) { goto ret; /* prev matched, but next did not */ } ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)izone, IPATCH_SF2_GEN_NOTE_RANGE, &z_noterange); ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)izone, IPATCH_SF2_GEN_VELOCITY_RANGE, &z_velrange); } /* Not previous/next or both of them match, check all items. */ for(p = children->items; p; p = p->next) { if(p->data == izone || ipatch_sf2_zone_peek_link_item(p->data) != (IpatchItem *)linked_sample) { continue; } ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)(p->data), IPATCH_SF2_GEN_NOTE_RANGE, &cmp_noterange); ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)(p->data), IPATCH_SF2_GEN_VELOCITY_RANGE, &cmp_velrange); if(!ipatch_sf2_gen_range_intersect_test(&z_noterange, &cmp_noterange) || !ipatch_sf2_gen_range_intersect_test(&z_velrange, &cmp_velrange)) { continue; } linked_izone = g_object_ref(p->data); break; } ret: if(children) { g_object_unref(children); /* -- unref children */ } if(parent) { g_object_unref(parent); /* -- unref parent */ } if(linked_sample) { g_object_unref(linked_sample); /* -- unref linked sample */ } g_object_unref(sample); /* -- unref sample */ return (linked_izone); } libinstpatch-1.1.6/libinstpatch/IpatchSF2IZone.h000066400000000000000000000047721400263525300215420ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_IZONE_H__ #define __IPATCH_SF2_IZONE_H__ #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchSF2IZone IpatchSF2IZone; typedef struct _IpatchSF2IZoneClass IpatchSF2IZoneClass; #define IPATCH_TYPE_SF2_IZONE (ipatch_sf2_izone_get_type ()) #define IPATCH_SF2_IZONE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SF2_IZONE, \ IpatchSF2IZone)) #define IPATCH_SF2_IZONE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SF2_IZONE, \ IpatchSF2IZoneClass)) #define IPATCH_IS_SF2_IZONE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SF2_IZONE)) #define IPATCH_IS_SF2_IZONE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SF2_IZONE)) #define IPATCH_SF2_IZONE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SF2_IZONE, \ IpatchSF2IZoneClass)) /* SoundFont izone item */ struct _IpatchSF2IZone { IpatchSF2Zone parent_instance; }; struct _IpatchSF2IZoneClass { IpatchSF2ZoneClass parent_class; }; /* reserve 2 flags */ #define IPATCH_SF2_IZONE_UNUSED_FLAG_SHIFT \ (IPATCH_SF2_ZONE_UNUSED_FLAG_SHIFT + 2) GType ipatch_sf2_izone_get_type(void); IpatchSF2IZone *ipatch_sf2_izone_new(void); IpatchSF2IZone *ipatch_sf2_izone_first(IpatchIter *iter); IpatchSF2IZone *ipatch_sf2_izone_next(IpatchIter *iter); void ipatch_sf2_izone_set_sample(IpatchSF2IZone *izone, IpatchSF2Sample *sample); IpatchSF2Sample *ipatch_sf2_izone_get_sample(IpatchSF2IZone *izone); IpatchSF2IZone *ipatch_sf2_izone_get_stereo_link(IpatchSF2IZone *izone); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2Inst.c000066400000000000000000000357361400263525300214320ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2Inst * @short_description: SoundFont instrument object * @see_also: #IpatchSF2, #IpatchSF2PZone * @stability: Stable * * SoundFont instruments are children of #IpatchSF2 objects and are referenced * by #IpatchSF2PZone objects. */ #include #include #include #include #include "IpatchSF2Inst.h" #include "IpatchSF2IZone.h" #include "IpatchSF2GenItem.h" #include "IpatchSF2ModItem.h" #include "IpatchSF2.h" #include "IpatchSF2File.h" #include "IpatchParamProp.h" #include "IpatchTypeProp.h" #include "ipatch_priv.h" /* properties */ enum { /* generator IDs are used for lower numbers */ PROP_NAME = IPATCH_SF2_GEN_ITEM_FIRST_PROP_USER_ID, PROP_MODULATORS }; static void ipatch_sf2_inst_class_init(IpatchSF2InstClass *klass); static void ipatch_sf2_inst_gen_item_iface_init(IpatchSF2GenItemIface *genitem_iface); static void ipatch_sf2_inst_mod_item_iface_init(IpatchSF2ModItemIface *moditem_iface); static void ipatch_sf2_inst_init(IpatchSF2Inst *inst); static void ipatch_sf2_inst_finalize(GObject *gobject); static void ipatch_sf2_inst_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sf2_inst_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sf2_inst_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static void ipatch_sf2_inst_item_remove_full(IpatchItem *item, gboolean full); static const GType *ipatch_sf2_inst_container_child_types(void); static gboolean ipatch_sf2_inst_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type); static void ipatch_sf2_inst_real_set_name(IpatchSF2Inst *inst, const char *name, gboolean name_notify); static gpointer parent_class = NULL; static GType inst_child_types[2] = { 0 }; static GParamSpec *name_pspec; /* For passing data from class init to gen item interface init */ static GParamSpec **gen_item_specs = NULL; static GParamSpec **gen_item_setspecs = NULL; /* For passing between class init and mod item interface init */ static GParamSpec *modulators_spec = NULL; GType ipatch_sf2_inst_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchSF2InstClass), NULL, NULL, (GClassInitFunc)ipatch_sf2_inst_class_init, NULL, NULL, sizeof(IpatchSF2Inst), 0, (GInstanceInitFunc)ipatch_sf2_inst_init, }; static const GInterfaceInfo genitem_iface = { (GInterfaceInitFunc) ipatch_sf2_inst_gen_item_iface_init, NULL, NULL }; static const GInterfaceInfo moditem_iface = { (GInterfaceInitFunc) ipatch_sf2_inst_mod_item_iface_init, NULL, NULL }; item_type = g_type_register_static(IPATCH_TYPE_CONTAINER, "IpatchSF2Inst", &item_info, 0); g_type_add_interface_static(item_type, IPATCH_TYPE_SF2_GEN_ITEM, &genitem_iface); g_type_add_interface_static(item_type, IPATCH_TYPE_SF2_MOD_ITEM, &moditem_iface); } return (item_type); } static void ipatch_sf2_inst_class_init(IpatchSF2InstClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->finalize = ipatch_sf2_inst_finalize; obj_class->get_property = ipatch_sf2_inst_get_property; /* we use the IpatchItem item_set_property method */ item_class->item_set_property = ipatch_sf2_inst_set_property; item_class->copy = ipatch_sf2_inst_item_copy; item_class->remove_full = ipatch_sf2_inst_item_remove_full; container_class->child_types = ipatch_sf2_inst_container_child_types; container_class->init_iter = ipatch_sf2_inst_container_init_iter; g_object_class_override_property(obj_class, PROP_NAME, "title"); name_pspec = ipatch_param_set(g_param_spec_string("name", "Name", "Name", NULL, G_PARAM_READWRITE | IPATCH_PARAM_UNIQUE), "string-max-length", IPATCH_SFONT_NAME_SIZE, /* max string length */ NULL); g_object_class_install_property(obj_class, PROP_NAME, name_pspec); g_object_class_override_property(obj_class, PROP_MODULATORS, "modulators"); modulators_spec = g_object_class_find_property(obj_class, "modulators"); inst_child_types[0] = IPATCH_TYPE_SF2_IZONE; /* install generator properties */ ipatch_sf2_gen_item_iface_install_properties(obj_class, IPATCH_SF2_GEN_PROPS_INST_GLOBAL, &gen_item_specs, &gen_item_setspecs); } /* gen item interface initialization */ static void ipatch_sf2_inst_gen_item_iface_init(IpatchSF2GenItemIface *genitem_iface) { genitem_iface->genarray_ofs = G_STRUCT_OFFSET(IpatchSF2Inst, genarray); genitem_iface->propstype = IPATCH_SF2_GEN_PROPS_INST_GLOBAL; g_return_if_fail(gen_item_specs != NULL); g_return_if_fail(gen_item_setspecs != NULL); memcpy(&genitem_iface->specs, gen_item_specs, sizeof(genitem_iface->specs)); memcpy(&genitem_iface->setspecs, gen_item_setspecs, sizeof(genitem_iface->setspecs)); g_free(gen_item_specs); g_free(gen_item_setspecs); } /* mod item interface initialization */ static void ipatch_sf2_inst_mod_item_iface_init(IpatchSF2ModItemIface *moditem_iface) { moditem_iface->modlist_ofs = G_STRUCT_OFFSET(IpatchSF2Inst, mods); /* cache the modulators property for fast notifications */ moditem_iface->mod_pspec = modulators_spec; } static void ipatch_sf2_inst_init(IpatchSF2Inst *inst) { inst->name = NULL; inst->zones = NULL; ipatch_sf2_gen_array_init(&inst->genarray, FALSE, FALSE); } static void ipatch_sf2_inst_finalize(GObject *gobject) { IpatchSF2Inst *inst = IPATCH_SF2_INST(gobject); IPATCH_ITEM_WLOCK(inst); g_free(inst->name); inst->name = NULL; ipatch_sf2_mod_list_free(inst->mods, TRUE); inst->mods = NULL; IPATCH_ITEM_WUNLOCK(inst); if(G_OBJECT_CLASS(parent_class)->finalize) { G_OBJECT_CLASS(parent_class)->finalize(gobject); } } static void ipatch_sf2_inst_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSF2Inst *inst = IPATCH_SF2_INST(object); IpatchSF2ModList *list; /* generator property? */ if(ipatch_sf2_gen_item_iface_set_property((IpatchSF2GenItem *)inst, property_id, value)) { return; } switch(property_id) { case PROP_NAME: ipatch_sf2_inst_real_set_name(inst, g_value_get_string(value), FALSE); break; case PROP_MODULATORS: list = (IpatchSF2ModList *)g_value_get_boxed(value); ipatch_sf2_mod_item_set_mods(IPATCH_SF2_MOD_ITEM(inst), list, IPATCH_SF2_MOD_NO_NOTIFY); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_sf2_inst_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSF2Inst *inst = IPATCH_SF2_INST(object); IpatchSF2ModList *list; /* generator property? */ if(ipatch_sf2_gen_item_iface_get_property((IpatchSF2GenItem *)inst, property_id, value)) { return; } switch(property_id) { case PROP_NAME: g_value_take_string(value, ipatch_sf2_inst_get_name(inst)); break; case PROP_MODULATORS: list = ipatch_sf2_mod_item_get_mods(IPATCH_SF2_MOD_ITEM(inst)); g_value_take_boxed(value, list); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_sf2_inst_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchSF2Inst *src_inst, *dest_inst; IpatchItem *zitem; GSList *p; src_inst = IPATCH_SF2_INST(src); dest_inst = IPATCH_SF2_INST(dest); IPATCH_ITEM_RLOCK(src_inst); dest_inst->name = g_strdup(src_inst->name); dest_inst->genarray = src_inst->genarray; dest_inst->mods = ipatch_sf2_mod_list_duplicate(src_inst->mods); p = src_inst->zones; while(p) /* duplicate zones */ { /* ++ ref new instrument zone, !! zone list takes it over */ zitem = ipatch_item_duplicate_link_func(IPATCH_ITEM(p->data), link_func, user_data); dest_inst->zones = g_slist_prepend(dest_inst->zones, zitem); ipatch_item_set_parent(zitem, IPATCH_ITEM(dest_inst)); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(src_inst); dest_inst->zones = g_slist_reverse(dest_inst->zones); } static void ipatch_sf2_inst_item_remove_full(IpatchItem *item, gboolean full) { IpatchItem *zitem, *temp; IpatchList *list; IpatchIter iter; list = ipatch_sf2_get_zone_references(item); /* ++ ref zone list */ ipatch_list_init_iter(list, &iter); zitem = ipatch_item_first(&iter); while(zitem) { temp = zitem; zitem = ipatch_item_next(&iter); ipatch_item_remove(temp); } g_object_unref(list); /* -- unref list */ if(IPATCH_ITEM_CLASS(parent_class)->remove_full) { IPATCH_ITEM_CLASS(parent_class)->remove_full(item, full); } } static const GType * ipatch_sf2_inst_container_child_types(void) { return (inst_child_types); } /* container is locked by caller */ static gboolean ipatch_sf2_inst_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type) { IpatchSF2Inst *inst = IPATCH_SF2_INST(container); if(!g_type_is_a(type, IPATCH_TYPE_SF2_IZONE)) { g_critical("Invalid child type '%s' for parent of type '%s'", g_type_name(type), g_type_name(G_OBJECT_TYPE(container))); return (FALSE); } ipatch_iter_GSList_init(iter, &inst->zones); return (TRUE); } /** * ipatch_sf2_inst_new: * * Create a new SoundFont instrument object. * * Returns: New SoundFont instrument with a reference count of 1. Caller * owns the reference and removing it will destroy the item, unless another * reference is added (if its parented for example). */ IpatchSF2Inst * ipatch_sf2_inst_new(void) { return (IPATCH_SF2_INST(g_object_new(IPATCH_TYPE_SF2_INST, NULL))); } /** * ipatch_sf2_inst_first: (skip) * @iter: Patch item iterator containing #IpatchSF2Inst items * * Gets the first item in an instrument iterator. A convenience wrapper for * ipatch_iter_first(). * * Returns: The first instrument in @iter or %NULL if empty. */ IpatchSF2Inst * ipatch_sf2_inst_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_SF2_INST(obj)); } else { return (NULL); } } /** * ipatch_sf2_inst_next: (skip) * @iter: Patch item iterator containing #IpatchSF2Inst items * * Gets the next item in an instrument iterator. A convenience wrapper for * ipatch_iter_next(). * * Returns: The next instrument in @iter or %NULL if at the end of the list. */ IpatchSF2Inst * ipatch_sf2_inst_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_SF2_INST(obj)); } else { return (NULL); } } /** * ipatch_sf2_inst_new_zone: * @inst: SoundFont instrument * @sample: Referenced sample for new zone * * A convenience function for quickly creating a new instrument zone, adding it * to @inst and setting the zone's referenced sample to @sample. */ void ipatch_sf2_inst_new_zone(IpatchSF2Inst *inst, IpatchSF2Sample *sample) { IpatchSF2IZone *izone; g_return_if_fail(IPATCH_IS_SF2_INST(inst)); g_return_if_fail(IPATCH_IS_SF2_SAMPLE(sample)); izone = ipatch_sf2_izone_new(); /* ++ ref new zone */ ipatch_sf2_zone_set_link_item(IPATCH_SF2_ZONE(izone), IPATCH_ITEM(sample)); ipatch_container_append(IPATCH_CONTAINER(inst), IPATCH_ITEM(izone)); g_object_unref(izone); /* -- unref zone */ } /** * ipatch_sf2_inst_set_name: * @inst: Instrument to set name of * @name: Value to set name to * * Sets the name of a SoundFont instrument. */ void ipatch_sf2_inst_set_name(IpatchSF2Inst *inst, const char *name) { g_return_if_fail(IPATCH_IS_SF2_INST(inst)); ipatch_sf2_inst_real_set_name(inst, name, TRUE); } /* the real inst name set routine */ static void ipatch_sf2_inst_real_set_name(IpatchSF2Inst *inst, const char *name, gboolean name_notify) { GValue oldval = { 0 }, newval = { 0 }; char *newname, *oldname; newname = g_strdup(name); IPATCH_ITEM_WLOCK(inst); oldname = inst->name; inst->name = newname; IPATCH_ITEM_WUNLOCK(inst); g_value_init(&oldval, G_TYPE_STRING); g_value_take_string(&oldval, oldname); g_value_init(&newval, G_TYPE_STRING); g_value_set_static_string(&newval, name); if(name_notify) { ipatch_item_prop_notify((IpatchItem *)inst, name_pspec, &newval, &oldval); } ipatch_item_prop_notify((IpatchItem *)inst, ipatch_item_pspec_title, &newval, &oldval); g_value_unset(&oldval); g_value_unset(&newval); } /** * ipatch_sf2_inst_get_name: * @inst: Instrument to get name of * * Gets the name of a SoundFont instrument. * * Returns: Name of instrument or %NULL if not set. String value should be * freed when finished with it. */ char * ipatch_sf2_inst_get_name(IpatchSF2Inst *inst) { char *name = NULL; g_return_val_if_fail(IPATCH_IS_SF2_INST(inst), NULL); IPATCH_ITEM_RLOCK(inst); if(inst->name) { name = g_strdup(inst->name); } IPATCH_ITEM_RUNLOCK(inst); return (name); } libinstpatch-1.1.6/libinstpatch/IpatchSF2Inst.h000066400000000000000000000052751400263525300214320ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_INST_H__ #define __IPATCH_SF2_INST_H__ #include #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchSF2Inst IpatchSF2Inst; typedef struct _IpatchSF2InstClass IpatchSF2InstClass; #define IPATCH_TYPE_SF2_INST (ipatch_sf2_inst_get_type ()) #define IPATCH_SF2_INST(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SF2_INST, \ IpatchSF2Inst)) #define IPATCH_SF2_INST_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SF2_INST, \ IpatchSF2InstClass)) #define IPATCH_IS_SF2_INST(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SF2_INST)) #define IPATCH_IS_SF2_INST_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SF2_INST)) #define IPATCH_SF2_INST_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SF2_INST, \ IpatchSF2InstClass)) /* SoundFont instrument item */ struct _IpatchSF2Inst { IpatchContainer parent_instance; char *name; /* name of inst */ GSList *zones; /* list of inst zones */ GSList *mods; /* modulators for global zone */ IpatchSF2GenArray genarray; /* generator array for global zone */ }; struct _IpatchSF2InstClass { IpatchContainerClass parent_class; }; GType ipatch_sf2_inst_get_type(void); IpatchSF2Inst *ipatch_sf2_inst_new(void); #define ipatch_sf2_inst_get_zones(inst) \ ipatch_container_get_children (IPATCH_CONTAINER (inst), \ IPATCH_TYPE_SF2_ZONE) IpatchSF2Inst *ipatch_sf2_inst_first(IpatchIter *iter); IpatchSF2Inst *ipatch_sf2_inst_next(IpatchIter *iter); void ipatch_sf2_inst_new_zone(IpatchSF2Inst *inst, IpatchSF2Sample *sample); void ipatch_sf2_inst_set_name(IpatchSF2Inst *inst, const char *name); char *ipatch_sf2_inst_get_name(IpatchSF2Inst *inst); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2Mod.c000066400000000000000000000045151400263525300212230ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Moderal Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Moderal Public License for more details. * * You should have received a copy of the GNU Moderal Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2Mod * @short_description: SoundFont modulators * @see_also: * @stability: Stable * * SoundFont modulators are used to define real time MIDI effect controls. */ #include #include #include "IpatchSF2Mod.h" #include "ipatch_priv.h" GType ipatch_sf2_mod_get_type(void) { static GType type = 0; if(!type) type = g_boxed_type_register_static("IpatchSF2Mod", (GBoxedCopyFunc)ipatch_sf2_mod_duplicate, (GBoxedFreeFunc)ipatch_sf2_mod_free); return (type); } /** * ipatch_sf2_mod_new: (skip) * * Create a new modulator * * Returns: New modulator */ IpatchSF2Mod * ipatch_sf2_mod_new(void) { return (g_slice_new0(IpatchSF2Mod)); } /** * ipatch_sf2_mod_free: (skip) * @mod: Modulator to free, should not be referenced by any zones. * * Free an #IpatchSF2Mod structure */ void ipatch_sf2_mod_free(IpatchSF2Mod *mod) { g_return_if_fail(mod != NULL); g_slice_free(IpatchSF2Mod, mod); } /** * ipatch_sf2_mod_duplicate: (skip) * @mod: Modulator to duplicate * * Duplicate a modulator * * Returns: New duplicate modulator */ IpatchSF2Mod * ipatch_sf2_mod_duplicate(const IpatchSF2Mod *mod) { IpatchSF2Mod *newmod; g_return_val_if_fail(mod != NULL, NULL); newmod = ipatch_sf2_mod_new(); newmod->src = mod->src; newmod->dest = mod->dest; newmod->amount = mod->amount; newmod->amtsrc = mod->amtsrc; newmod->trans = mod->trans; return (newmod); } libinstpatch-1.1.6/libinstpatch/IpatchSF2Mod.h000066400000000000000000000105641400263525300212310ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Moderal Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Moderal Public License for more details. * * You should have received a copy of the GNU Moderal Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_MOD_H__ #define __IPATCH_SF2_MOD_H__ #include #include #include /* forward type declarations */ typedef struct _IpatchSF2Mod IpatchSF2Mod; /* IpatchSF2Mod has a glib boxed type */ #define IPATCH_TYPE_SF2_MOD (ipatch_sf2_mod_get_type ()) /* modulator structure */ struct _IpatchSF2Mod { guint16 src; /* source modulator (MIDI controller, etc) */ guint16 dest; /* destination generator */ gint16 amount; /* degree of modulation */ guint16 amtsrc; /* second source controls amount of first */ guint16 trans; /* transform function applied to source */ }; #include // For backwards compatibility where IpatchSF2Mod and IpatchSF2ModList were combined /* Compare two modulators to see if they are identical and can be combined (all fields except amount must be identical). Returns TRUE if identical, FALSE otherwise */ #define IPATCH_SF2_MOD_ARE_IDENTICAL(a, b) \ ((a)->src == (b)->src && (a)->dest == (b)->dest \ && (a)->amtsrc == (b)->amtsrc && (a)->trans == (b)->trans) /* like IPATCH_SF2_MOD_ARE_IDENTICAL but also checks if amounts are identical */ #define IPATCH_SF2_MOD_ARE_IDENTICAL_AMOUNT(a, b) \ ((a)->src == (b)->src && (a)->dest == (b)->dest \ && (a)->amtsrc == (b)->amtsrc && (a)->trans == (b)->trans \ && (a)->amount == (b)->amount) /* modulator flags */ typedef enum { IPATCH_SF2_MOD_MASK_CONTROL = 0x007F, IPATCH_SF2_MOD_MASK_CC = 0x0080, IPATCH_SF2_MOD_MASK_DIRECTION = 0x0100, IPATCH_SF2_MOD_MASK_POLARITY = 0x0200, IPATCH_SF2_MOD_MASK_TYPE = 0xFC00 } IpatchSF2ModFieldMasks; typedef enum { IPATCH_SF2_MOD_SHIFT_CONTROL = 0, IPATCH_SF2_MOD_SHIFT_CC = 7, IPATCH_SF2_MOD_SHIFT_DIRECTION = 8, IPATCH_SF2_MOD_SHIFT_POLARITY = 9, IPATCH_SF2_MOD_SHIFT_TYPE = 10 } IpatchSF2ModFieldShifts; typedef enum { IPATCH_SF2_MOD_CONTROL_NONE = 0, IPATCH_SF2_MOD_CONTROL_NOTE_ON_VELOCITY = 2, IPATCH_SF2_MOD_CONTROL_NOTE_NUMBER = 3, IPATCH_SF2_MOD_CONTROL_POLY_PRESSURE = 10, IPATCH_SF2_MOD_CONTROL_CHAN_PRESSURE = 13, IPATCH_SF2_MOD_CONTROL_PITCH_WHEEL = 14, IPATCH_SF2_MOD_CONTROL_BEND_RANGE = 16 } IpatchSF2ModControl; typedef enum { IPATCH_SF2_MOD_CC_GENERAL = (0 << IPATCH_SF2_MOD_SHIFT_CC), IPATCH_SF2_MOD_CC_MIDI = (1 << IPATCH_SF2_MOD_SHIFT_CC) } IpatchSF2ModControlPalette; typedef enum { IPATCH_SF2_MOD_DIRECTION_POSITIVE = (0 << IPATCH_SF2_MOD_SHIFT_DIRECTION), IPATCH_SF2_MOD_DIRECTION_NEGATIVE = (1 << IPATCH_SF2_MOD_SHIFT_DIRECTION) } IpatchSF2ModDirection; typedef enum { IPATCH_SF2_MOD_POLARITY_UNIPOLAR = (0 << IPATCH_SF2_MOD_SHIFT_POLARITY), IPATCH_SF2_MOD_POLARITY_BIPOLAR = (1 << IPATCH_SF2_MOD_SHIFT_POLARITY) } IpatchSF2ModPolarity; typedef enum { IPATCH_SF2_MOD_TYPE_LINEAR = (0 << IPATCH_SF2_MOD_SHIFT_TYPE), IPATCH_SF2_MOD_TYPE_CONCAVE = (1 << IPATCH_SF2_MOD_SHIFT_TYPE), IPATCH_SF2_MOD_TYPE_CONVEX = (2 << IPATCH_SF2_MOD_SHIFT_TYPE), IPATCH_SF2_MOD_TYPE_SWITCH = (3 << IPATCH_SF2_MOD_SHIFT_TYPE) } IpatchSF2ModType; typedef enum { IPATCH_SF2_MOD_TRANSFORM_LINEAR = 0 } IpatchSF2ModTransform; /* flags for ipatch_sf2_mod_class_set_mods() */ typedef enum { IPATCH_SF2_MOD_NO_DUPLICATE = 1 << 0, /* don't duplicate mod list (owned!) */ IPATCH_SF2_MOD_NO_NOTIFY = 1 << 1 /* don't do item property notify */ } IpatchSF2ModFlags; GType ipatch_sf2_mod_get_type(void); IpatchSF2Mod *ipatch_sf2_mod_new(void); void ipatch_sf2_mod_free(IpatchSF2Mod *mod); IpatchSF2Mod *ipatch_sf2_mod_duplicate(const IpatchSF2Mod *mod); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2ModItem.c000066400000000000000000000331201400263525300220340ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Moderal Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Moderal Public License for more details. * * You should have received a copy of the GNU Moderal Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2ModItem * @short_description: SoundFont modulator item interface * @see_also: * @stability: Stable * * An interface type which is used by #IpatchSF2Preset, #IpatchSF2Inst, * #IpatchSF2PZone and #IpatchSF2IZone objects to add modulator realtime effect * functionality. */ #include #include #include "IpatchSF2ModItem.h" #include "IpatchSF2Gen.h" #include "ipatch_priv.h" static void ipatch_sf2_mod_item_iface_init(IpatchSF2ModItemIface *iface); GType ipatch_sf2_mod_item_get_type(void) { static GType itype = 0; if(!itype) { static const GTypeInfo info = { sizeof(IpatchSF2ModItemIface), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) ipatch_sf2_mod_item_iface_init, (GClassFinalizeFunc) NULL }; itype = g_type_register_static(G_TYPE_INTERFACE, "IpatchSF2ModItem", &info, 0); /* IpatchSF2ModItemIface types must be IpatchItem objects (for locking) */ g_type_interface_add_prerequisite(itype, IPATCH_TYPE_ITEM); } return (itype); } static void ipatch_sf2_mod_item_iface_init(IpatchSF2ModItemIface *iface) { /** * IpatchSF2ModItem:modulators: (type GSList(Ipatch.SF2Mod)) * * GSList of IpatchSF2Mod modulators. */ g_object_interface_install_property(iface, g_param_spec_boxed("modulators", _("Modulators"), _("Modulators"), IPATCH_TYPE_SF2_MOD_LIST, G_PARAM_READWRITE)); } /** * ipatch_sf2_mod_item_get_mods: * @item: Item with modulators * * Gets a list of modulators from an item with modulators. List should be freed * with ipatch_sf2_mod_list_free() (free_mods set to %TRUE) when finished * with it. * * Returns: (element-type Ipatch.SF2Mod) (transfer full) (nullable): * New list of modulators (#IpatchSF2Mod) in @item or %NULL if no modulators. * Remember to free it with ipatch_sf2_mod_list_free() when finished. */ GSList * ipatch_sf2_mod_item_get_mods(IpatchSF2ModItem *item) { IpatchSF2ModItemIface *iface; GSList **pmods, *newlist = NULL; IpatchSF2Mod *mod; GSList *p; g_return_val_if_fail(IPATCH_IS_SF2_MOD_ITEM(item), NULL); /* get pointer to GSList from IpatchSF2ModItemIface->modlist_ofs */ iface = IPATCH_SF2_MOD_ITEM_GET_IFACE(item); g_return_val_if_fail(iface->modlist_ofs != 0, NULL); pmods = (GSList **)G_STRUCT_MEMBER_P(item, iface->modlist_ofs); IPATCH_ITEM_RLOCK(item); p = *pmods; while(p) { mod = ipatch_sf2_mod_duplicate((IpatchSF2Mod *)(p->data)); newlist = g_slist_prepend(newlist, mod); p = p->next; } IPATCH_ITEM_RUNLOCK(item); newlist = g_slist_reverse(newlist); return (newlist); } /** * ipatch_sf2_mod_item_set_mods: (skip) * @item: Item with modulators * @mod_list: (element-type Ipatch.SF2Mod): Modulator list to assign to zone. * @flags: (type IpatchSF2ModFlags): Flags for controlling list duplication and item property * notification (#IpatchSF2ModFlags). If #IPATCH_SF2_MOD_NO_DUPLICATE * is set then ownership of @mod_list is taken over (not duplicated). * If #IPATCH_SF2_MOD_NO_NOTIFY is set, then item property notify will not * be done. * * Sets the complete modulator list of an item with modulators. * If #IPATCH_SF2_MOD_NO_NOTIFY is not in @flags then #IpatchItem property * notify is done. */ void ipatch_sf2_mod_item_set_mods(IpatchSF2ModItem *item, GSList *mod_list, int flags) { GValue old_value = { 0 }, new_value = { 0 }; GSList **pmods, *oldlist, *newlist; IpatchSF2ModItemIface *iface; g_return_if_fail(IPATCH_IS_SF2_MOD_ITEM(item)); /* get pointer to GSList from IpatchSF2ModItemIface->modlist_ofs */ iface = IPATCH_SF2_MOD_ITEM_GET_IFACE(item); g_return_if_fail(iface->modlist_ofs != 0); pmods = (GSList **)G_STRUCT_MEMBER_P(item, iface->modlist_ofs); if(!(flags & IPATCH_SF2_MOD_NO_DUPLICATE)) { newlist = ipatch_sf2_mod_list_duplicate(mod_list); // ++ Duplicate mod_list } else { newlist = mod_list; // !! Just use mod_list directly } if(!(flags & IPATCH_SF2_MOD_NO_NOTIFY)) { mod_list = ipatch_sf2_mod_list_duplicate(mod_list); // ++ Duplicate mod_list for notify } IPATCH_ITEM_WLOCK(item); oldlist = *pmods; *pmods = newlist; IPATCH_ITEM_WUNLOCK(item); /* do property notify if NO_NOTIFY flag not set */ if(!(flags & IPATCH_SF2_MOD_NO_NOTIFY)) { g_value_init(&old_value, IPATCH_TYPE_SF2_MOD_LIST); g_value_take_boxed(&old_value, oldlist); // -- Take over oldlist g_value_init(&new_value, IPATCH_TYPE_SF2_MOD_LIST); g_value_take_boxed(&new_value, mod_list); // -- Take over mod_list ipatch_item_prop_notify(IPATCH_ITEM(item), iface->mod_pspec, &new_value, &old_value); g_value_unset(&new_value); g_value_unset(&old_value); } else { ipatch_sf2_mod_list_free(oldlist, TRUE); // -- free old list if no notify } } /** * ipatch_sf2_mod_item_set_mods_copy: (rename-to ipatch_sf2_mod_item_set_mods) * @item: Item with modulators * @mod_list: (element-type Ipatch.SF2Mod) (transfer none) (nullable): Modulator list to assign to zone. * * Sets the modulator list of an item with modulators. */ void ipatch_sf2_mod_item_set_mods_copy(IpatchSF2ModItem *item, GSList *mod_list) { ipatch_sf2_mod_item_set_mods(item, mod_list, 0); } /** * ipatch_sf2_mod_item_add_mod: * @item: Item with modulators * @mod: (transfer none): Modulator to append to end of @item object's modulator list * * Append a modulator to an item's modulator list. * NOTE: Does not check for duplicates! */ void ipatch_sf2_mod_item_add(IpatchSF2ModItem *item, const IpatchSF2Mod *mod) { ipatch_sf2_mod_item_insert(item, mod, -1); } /** * ipatch_sf2_mod_item_insert: * @item: Item with modulators * @mod: (transfer none): Modulator to insert * @pos: Index position in zone's modulator list to insert * (0 = first, < 0 = last) * * Inserts a modulator into an item's modulator list. * NOTE: Does not check for duplicates! */ void ipatch_sf2_mod_item_insert(IpatchSF2ModItem *item, const IpatchSF2Mod *mod, int pos) { GValue old_value = { 0 }, new_value = { 0 }; GSList **pmods, *oldlist, *newlist; IpatchSF2ModItemIface *iface; IpatchSF2Mod *newmod; g_return_if_fail(IPATCH_IS_SF2_MOD_ITEM(item)); g_return_if_fail(mod != NULL); /* get pointer to GSList from IpatchSF2ModItemIface->modlist_ofs */ iface = IPATCH_SF2_MOD_ITEM_GET_IFACE(item); g_return_if_fail(iface->modlist_ofs != 0); pmods = (GSList **)G_STRUCT_MEMBER_P(item, iface->modlist_ofs); newmod = ipatch_sf2_mod_duplicate(mod); // ++ duplicate the new modulator IPATCH_ITEM_WLOCK(item); newlist = ipatch_sf2_mod_list_duplicate(*pmods); // ++ Duplicate current list newlist = g_slist_insert(newlist, newmod, pos); oldlist = *pmods; *pmods = newlist; // !! Item takes over new modulator list newlist = ipatch_sf2_mod_list_duplicate(newlist); // ++ Duplicate newlist for notify IPATCH_ITEM_WUNLOCK(item); g_value_init(&old_value, IPATCH_TYPE_SF2_MOD_LIST); g_value_take_boxed(&old_value, oldlist); // -- Take over oldlist g_value_init(&new_value, IPATCH_TYPE_SF2_MOD_LIST); g_value_take_boxed(&new_value, newlist); // -- Take over newlist ipatch_item_prop_notify(IPATCH_ITEM(item), iface->mod_pspec, &new_value, &old_value); g_value_unset(&new_value); g_value_unset(&old_value); } /** * ipatch_sf2_mod_item_remove: * @item: Item with modulators * @mod: (transfer none): Matching values of modulator to remove * * Remove a modulator from an item with modulators. The modulator values in @mod * are used to search the modulator list. The first modulator * that matches all fields in @mod is removed. */ void ipatch_sf2_mod_item_remove(IpatchSF2ModItem *item, const IpatchSF2Mod *mod) { GValue old_value = { 0 }, new_value = { 0 }; GSList **pmods, *oldlist, *newlist; IpatchSF2ModItemIface *iface; gboolean changed; g_return_if_fail(IPATCH_IS_SF2_MOD_ITEM(item)); g_return_if_fail(mod != NULL); /* get pointer to GSList from IpatchSF2ModItemIface->modlist_ofs */ iface = IPATCH_SF2_MOD_ITEM_GET_IFACE(item); g_return_if_fail(iface->modlist_ofs != 0); pmods = (GSList **)G_STRUCT_MEMBER_P(item, iface->modlist_ofs); IPATCH_ITEM_WLOCK(item); newlist = ipatch_sf2_mod_list_duplicate(*pmods); // ++ Duplicate current list newlist = ipatch_sf2_mod_list_remove(newlist, mod, &changed); // Remove the modulator oldlist = *pmods; *pmods = newlist; // !! Item takes over new modulator list if(changed) { newlist = ipatch_sf2_mod_list_duplicate(newlist); // ++ Duplicate newlist for notify } IPATCH_ITEM_WUNLOCK(item); if(changed) { g_value_init(&old_value, IPATCH_TYPE_SF2_MOD_LIST); g_value_take_boxed(&old_value, oldlist); // -- Take over oldlist g_value_init(&new_value, IPATCH_TYPE_SF2_MOD_LIST); g_value_take_boxed(&new_value, newlist); // -- Take over newlist ipatch_item_prop_notify(IPATCH_ITEM(item), iface->mod_pspec, &new_value, &old_value); g_value_unset(&new_value); g_value_unset(&old_value); } else { ipatch_sf2_mod_list_free(oldlist, TRUE); // -- free oldlist ipatch_sf2_mod_list_free(newlist, TRUE); // -- free newlist } } /** * ipatch_sf2_mod_item_change: * @item: Item with modulators * @oldmod: (transfer none): Current values of modulator to set * @newmod: (transfer none): New modulator values * * Sets the values of an existing modulator in an item with modulators. The * modulator list in item is searched for a modulator that matches the values in * @oldmod. If a modulator is found its values are set to those in @newmod. * If it is not found, nothing is done. */ void ipatch_sf2_mod_item_change(IpatchSF2ModItem *item, const IpatchSF2Mod *oldmod, const IpatchSF2Mod *newmod) { GValue old_value = { 0 }, new_value = { 0 }; GSList **pmods, *oldlist, *newlist; IpatchSF2ModItemIface *iface; gboolean changed; g_return_if_fail(IPATCH_IS_SF2_MOD_ITEM(item)); g_return_if_fail(oldmod != NULL); g_return_if_fail(newmod != NULL); /* get pointer to GSList from IpatchSF2ModItemIface->modlist_ofs */ iface = IPATCH_SF2_MOD_ITEM_GET_IFACE(item); g_return_if_fail(iface->modlist_ofs != 0); pmods = (GSList **)G_STRUCT_MEMBER_P(item, iface->modlist_ofs); IPATCH_ITEM_WLOCK(item); newlist = ipatch_sf2_mod_list_duplicate(*pmods); // ++ Duplicate current list changed = ipatch_sf2_mod_list_change(newlist, oldmod, newmod); // Change the modulator oldlist = *pmods; *pmods = newlist; // !! Item takes over new modulator list if(changed) { newlist = ipatch_sf2_mod_list_duplicate(newlist); // ++ Duplicate newlist for notify } IPATCH_ITEM_WUNLOCK(item); if(changed) { g_value_init(&old_value, IPATCH_TYPE_SF2_MOD_LIST); g_value_take_boxed(&old_value, oldlist); // -- Take over oldlist g_value_init(&new_value, IPATCH_TYPE_SF2_MOD_LIST); g_value_take_boxed(&new_value, newlist); // -- Take over newlist ipatch_item_prop_notify(IPATCH_ITEM(item), iface->mod_pspec, &new_value, &old_value); g_value_unset(&new_value); g_value_unset(&old_value); } else { ipatch_sf2_mod_list_free(oldlist, TRUE); // -- free oldlist ipatch_sf2_mod_list_free(newlist, TRUE); // -- free newlist } } /** * ipatch_sf2_mod_item_count: * @item: Item with modulators * * Count number of modulators in an item with modulators. * * Returns: Count of modulators */ guint ipatch_sf2_mod_item_count(IpatchSF2ModItem *item) { IpatchSF2ModItemIface *iface; GSList **pmods; guint i; g_return_val_if_fail(IPATCH_IS_SF2_MOD_ITEM(item), 0); /* get pointer to GSList from IpatchSF2ModItemIface->modlist_ofs */ iface = IPATCH_SF2_MOD_ITEM_GET_IFACE(item); g_return_val_if_fail(iface->modlist_ofs != 0, 0); pmods = (GSList **)G_STRUCT_MEMBER_P(item, iface->modlist_ofs); IPATCH_ITEM_RLOCK(item); i = g_slist_length(*pmods); IPATCH_ITEM_RUNLOCK(item); return (i); } libinstpatch-1.1.6/libinstpatch/IpatchSF2ModItem.h000066400000000000000000000054361400263525300220520ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Moderal Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Moderal Public License for more details. * * You should have received a copy of the GNU Moderal Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_MOD_ITEM_H__ #define __IPATCH_SF2_MOD_ITEM_H__ #include #include #include /* forward type declarations */ typedef struct _IpatchSF2ModItem IpatchSF2ModItem; // dummy typedef typedef struct _IpatchSF2ModItemIface IpatchSF2ModItemIface; #define IPATCH_TYPE_SF2_MOD_ITEM (ipatch_sf2_mod_item_get_type ()) #define IPATCH_SF2_MOD_ITEM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SF2_MOD_ITEM, \ IpatchSF2ModItem)) #define IPATCH_SF2_MOD_ITEM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SF2_MOD_ITEM, \ IpatchSF2ModItemIface)) #define IPATCH_IS_SF2_MOD_ITEM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SF2_MOD_ITEM)) #define IPATCH_SF2_MOD_ITEM_GET_IFACE(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IPATCH_TYPE_SF2_MOD_ITEM, \ IpatchSF2ModItemIface)) /* modulator item interface */ struct _IpatchSF2ModItemIface { GTypeInterface parent_iface; guint modlist_ofs; /* offset in item instance to modulator list pointer */ GParamSpec *mod_pspec; /* cached value of modulator pspec for fast notifies */ }; GType ipatch_sf2_mod_item_get_type(void); GSList *ipatch_sf2_mod_item_get_mods(IpatchSF2ModItem *item); void ipatch_sf2_mod_item_set_mods(IpatchSF2ModItem *item, GSList *mod_list, int flags); void ipatch_sf2_mod_item_set_mods_copy(IpatchSF2ModItem *item, GSList *mod_list); void ipatch_sf2_mod_item_add(IpatchSF2ModItem *item, const IpatchSF2Mod *mod); void ipatch_sf2_mod_item_insert(IpatchSF2ModItem *item, const IpatchSF2Mod *mod, int pos); void ipatch_sf2_mod_item_remove(IpatchSF2ModItem *item, const IpatchSF2Mod *mod); void ipatch_sf2_mod_item_change(IpatchSF2ModItem *item, const IpatchSF2Mod *oldmod, const IpatchSF2Mod *newmod); guint ipatch_sf2_mod_item_count(IpatchSF2ModItem *item); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2ModList.c000066400000000000000000000275361400263525300220670ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Moderal Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Moderal Public License for more details. * * You should have received a copy of the GNU Moderal Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2ModList * @short_description: SoundFont modulator lists * @see_also: * @stability: Stable * * SoundFont modulators are used to define real time MIDI effect controls. */ #include #include #include "IpatchSF2ModList.h" #include "IpatchSF2Gen.h" #include "ipatch_priv.h" /* default modulators */ static IpatchSF2Mod default_mods[] = { { 0x0502, IPATCH_SF2_GEN_ATTENUATION, 960, 0x0, 0 }, { 0x0102, IPATCH_SF2_GEN_FILTER_CUTOFF, -2400, 0xD02, 0 }, { 0x000D, IPATCH_SF2_GEN_VIB_LFO_TO_PITCH, 50, 0x0, 0 }, { 0x0081, IPATCH_SF2_GEN_VIB_LFO_TO_PITCH, 50, 0x0, 0 }, { 0x0587, IPATCH_SF2_GEN_ATTENUATION, 960, 0x0, 0 }, { 0x028A, IPATCH_SF2_GEN_PAN, 1000, 0x0, 0 }, { 0x058B, IPATCH_SF2_GEN_ATTENUATION, 960, 0x0, 0 }, { 0x00DB, IPATCH_SF2_GEN_REVERB, 200, 0x0, 0 }, { 0x00DD, IPATCH_SF2_GEN_CHORUS, 200, 0x0, 0 } // { 0x020E, InitialPitch WTF?, 12700, 0x0010, 0 }, }; static GSList *list = NULL; /* list of default modulators */ /* ----- Initialization/deinitialization of list ----------------------------*/ /* Initialize list */ void _ipatch_sf2_mod_list_init(void) { list = NULL; } /* Free list */ void _ipatch_sf2_mod_list_deinit(void) { g_slist_free(list); } /*------ IpatchSF2ModList object functions ---------------------------------*/ GType ipatch_sf2_mod_list_get_type(void) { static GType type = 0; if(!type) type = g_boxed_type_register_static("IpatchSF2ModList", (GBoxedCopyFunc)ipatch_sf2_mod_list_duplicate, (GBoxedFreeFunc)ipatch_sf2_mod_list_boxed_free); return (type); } /** * ipatch_sf2_mod_list_duplicate: (skip) * @list: (element-type Ipatch.SF2Mod) (transfer none): Modulator list to duplicate * * Duplicates a modulator list (list and modulator data). * * Returns: (element-type Ipatch.SF2Mod) (transfer full): New duplicate modulator list which * should be freed with ipatch_sf2_mod_list_free() with @free_mods set to * %TRUE when finished with it. */ GSList * ipatch_sf2_mod_list_duplicate(const GSList *list) { GSList *newlist = NULL; while(list) { newlist = g_slist_prepend(newlist, ipatch_sf2_mod_duplicate ((IpatchSF2Mod *)(list->data))); list = list->next; } newlist = g_slist_reverse(newlist); return (newlist); } /** * ipatch_sf2_mod_list_override: (skip) * @alist: First modulator list * @blist: Second modulator list * @copy: If %TRUE then modulator data is duplicated * * Creates a new modulator list by combining @alist and @blist. Modulators * in @blist override identical modulators in @alist. If @copy is set then * the modulator data is also duplicated (a new IpatchSF2ModList is created). * * Returns: New IpatchSF2ModList of combined modulator lists. * Should be freed with ipatch_sf2_mod_list_free() with the free_mods parameter * set to the value of @copy. */ GSList * ipatch_sf2_mod_list_override(const GSList *alist, const GSList *blist, gboolean copy) { GSList *newlist, *bcopy, *p; IpatchSF2Mod *amod, *bmod; if(copy) { newlist = ipatch_sf2_mod_list_duplicate(blist); } else { newlist = g_slist_copy((GSList *)blist); } if(!newlist) /* optimize for empty blist */ { if(copy) { return (ipatch_sf2_mod_list_duplicate(alist)); } else { return (g_slist_copy((GSList *)alist)); } } bcopy = newlist; while(alist) /* loop over alist */ { amod = (IpatchSF2Mod *)(alist->data); p = bcopy; while(p) { bmod = (IpatchSF2Mod *)(p->data); if(IPATCH_SF2_MOD_ARE_IDENTICAL(amod, bmod)) { break; } p = p->next; } if(!p) /* no duplicate found? */ newlist = g_slist_prepend(newlist, copy ? ipatch_sf2_mod_duplicate (amod) : amod); alist = alist->next; } return (newlist); } /** * ipatch_sf2_mod_list_override_copy: (rename-to ipatch_sf2_mod_list_override) * @alist: (element-type Ipatch.SF2Mod) (transfer none) (nullable): First modulator list * @blist: (element-type Ipatch.SF2Mod) (transfer none) (nullable): Second modulator list * * Creates a new modulator list by combining @alist and @blist. Modulators * in @blist override identical modulators in @alist. * * Returns: (element-type Ipatch.SF2Mod) (transfer full) (nullable): New IpatchSF2ModList of * combined modulator lists. Should be freed with ipatch_sf2_mod_list_free() with * the free_mods parameter set to %TRUE. * * Since: 1.1.0 */ GSList * ipatch_sf2_mod_list_override_copy(const GSList *alist, const GSList *blist) { return (ipatch_sf2_mod_list_override(alist, blist, TRUE)); } /** * ipatch_sf2_mod_list_offset: * @alist: (element-type Ipatch.SF2Mod) (transfer none) (nullable): First modulator list * @blist: (element-type Ipatch.SF2Mod) (transfer none) (nullable): Second modulator list * * Creates a new modulator list by combining @list and @blist. Modulators * in @blist offset (amounts are added) identical modulators in @alist. * Operation is non-destructive as a new list is created and modulator data * is duplicated. * * NOTE: Optimized for empty @blist. * * Returns: (element-type Ipatch.SF2Mod) (transfer full) (nullable): New IpatchSF2ModList * of combined modulator lists. Should be freed with ipatch_sf2_mod_list_free() * with @free_mods set to %TRUE when finished with it. */ GSList * ipatch_sf2_mod_list_offset(const GSList *alist, const GSList *blist) { GSList *newlist, *acopy, *p; IpatchSF2Mod *amod, *bmod; int add; newlist = ipatch_sf2_mod_list_duplicate(alist); if(!blist) { return (newlist); /* optimize for empty blist */ } acopy = newlist; while(blist) /* loop over alist */ { bmod = (IpatchSF2Mod *)(blist->data); p = acopy; while(p) { amod = (IpatchSF2Mod *)(p->data); if(IPATCH_SF2_MOD_ARE_IDENTICAL(amod, bmod)) { /* offset (add) the modulator amount */ add = amod->amount + bmod->amount; add = CLAMP(add, -32768, 32767); amod->amount = add; break; } p = p->next; } /* no duplicate found? */ if(!p) newlist = g_slist_prepend(newlist, ipatch_sf2_mod_duplicate(bmod)); blist = blist->next; } return (newlist); } /** * ipatch_sf2_mod_list_free: (skip) * @list: Modulator list to free * @free_mods: If %TRUE then the modulators themselves are freed, %FALSE * makes this function act just like g_slist_free() (only the list is * freed not the modulators). * * Free a list of modulators */ void ipatch_sf2_mod_list_free(GSList *list, gboolean free_mods) { GSList *p; if(free_mods) { p = list; while(p) { ipatch_sf2_mod_free((IpatchSF2Mod *)(p->data)); p = g_slist_delete_link(p, p); } } else { g_slist_free(list); } } /** * ipatch_sf2_mod_list_boxed_free: (skip) * @list: Modulator list to free * * Like ipatch_sf2_mod_list_free() but used for boxed type declaration and so * therefore frees all modulators in the list. */ void ipatch_sf2_mod_list_boxed_free(GSList *list) { ipatch_sf2_mod_list_free(list, TRUE); } /** * ipatch_sf2_mod_list_insert: (skip) * @mods: (element-type Ipatch.SF2Mod) (transfer none): Modulator list to insert into * @modvals: (transfer none): Modulator values to insert (a new modulator is created * and the values are copied to it) * @pos: Index position in zone's modulator list to insert (0 = first, < 0 = last) * * Inserts a modulator into a modulator list. Does not check for * duplicates! The modulator is not used directly, a new one is created and * the values in @mod are copied to it. * * Returns: New start (root) of @mods list. */ GSList * ipatch_sf2_mod_list_insert(GSList *mods, const IpatchSF2Mod *modvals, int pos) { IpatchSF2Mod *newmod; g_return_val_if_fail(modvals != NULL, mods); newmod = ipatch_sf2_mod_duplicate(modvals); return (g_slist_insert(mods, newmod, pos)); } /** * ipatch_sf2_mod_list_remove: (skip) * @mods: (transfer full): Modulator list to remove from * @modvals: Values of modulator to remove * @changed: (out) (optional): Pointer to store bool of whether the list was changed * (%NULL to ignore) * * Remove a modulator from a modulator list. The modulator values in @modvals * are used to search the modulator list. The first modulator * that matches all fields in @modvals is removed. * * Returns: New start (root) of @mods list. */ GSList * ipatch_sf2_mod_list_remove(GSList *mods, const IpatchSF2Mod *modvals, gboolean *changed) { IpatchSF2Mod *mod; GSList *p; if(changed) { *changed = FALSE; } g_return_val_if_fail(modvals != NULL, mods); for(p = mods; p; p = g_slist_next(p)) { mod = (IpatchSF2Mod *)(p->data); if(IPATCH_SF2_MOD_ARE_IDENTICAL_AMOUNT(mod, modvals)) { ipatch_sf2_mod_free(mod); if(changed) { *changed = TRUE; } return (g_slist_delete_link(mods, p)); } } return (mods); } /** * ipatch_sf2_mod_list_change: (skip) * @mods: Modulator list to change a modulator in * @oldvals: Current values of modulator to set * @newvals: New modulator values * * Sets the values of an existing modulator in a modulator list. The list * is searched for a modulator that matches the values in @oldvals. If a * modulator is found its values are set to those in @newvals. If it is not * found, nothing is done. * * Returns: %TRUE if changed, %FALSE otherwise (no match) */ gboolean ipatch_sf2_mod_list_change(GSList *mods, const IpatchSF2Mod *oldvals, const IpatchSF2Mod *newvals) { IpatchSF2Mod *mod; GSList *p; g_return_val_if_fail(oldvals != NULL, FALSE); g_return_val_if_fail(newvals != NULL, FALSE); for(p = mods; p; p = p->next) { mod = (IpatchSF2Mod *)(p->data); if(IPATCH_SF2_MOD_ARE_IDENTICAL_AMOUNT(mod, oldvals)) { *mod = *newvals; /* replace values in modulator */ return (TRUE); } } return (FALSE); } /** * ipatch_sf2_mod_list_get_default: * * Get the list of default instrument modulators. * * Returns: (element-type Ipatch.SF2Mod) (transfer none): The list of default * modulators. The same modulator list is returned on subsequent calls and * should not be modified or freed. */ G_CONST_RETURN GSList * ipatch_sf2_mod_list_get_default(void) { int i; if(!list) for(i = sizeof(default_mods) / sizeof(IpatchSF2Mod) - 1; i >= 0 ; i--) { list = g_slist_prepend(list, &default_mods[i]); } return (list); } libinstpatch-1.1.6/libinstpatch/IpatchSF2ModList.h000066400000000000000000000041161400263525300220610ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Moderal Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Moderal Public License for more details. * * You should have received a copy of the GNU Moderal Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_MOD_LIST_H__ #define __IPATCH_SF2_MOD_LIST_H__ #include #include #include #include /* forward type declarations */ typedef GSList IpatchSF2ModList; /* GSList has a glib boxed type */ #define IPATCH_TYPE_SF2_MOD_LIST (ipatch_sf2_mod_list_get_type ()) GType ipatch_sf2_mod_list_get_type(void); GSList *ipatch_sf2_mod_list_duplicate(const GSList *list); GSList *ipatch_sf2_mod_list_override(const GSList *alist, const GSList *blist, gboolean copy); GSList *ipatch_sf2_mod_list_override_copy(const GSList *alist, const GSList *blist); void ipatch_sf2_mod_list_free(GSList *list, gboolean free_mods); void ipatch_sf2_mod_list_boxed_free(GSList *list); GSList *ipatch_sf2_mod_list_insert(GSList *mods, const IpatchSF2Mod *modvals, int pos); GSList *ipatch_sf2_mod_list_remove(GSList *mods, const IpatchSF2Mod *modvals, gboolean *changed); gboolean ipatch_sf2_mod_list_change(GSList *mods, const IpatchSF2Mod *oldvals, const IpatchSF2Mod *newvals); GSList *ipatch_sf2_mod_list_offset(const GSList *alist, const GSList *blist); G_CONST_RETURN GSList *ipatch_sf2_mod_list_get_default(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2PZone.c000066400000000000000000000205511400263525300215350ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2PZone * @short_description: SoundFont preset zone object * @see_also: #IpatchSF2Preset, #IpatchSF2Inst * @stability: Stable * * Preset zones are children to #IpatchSF2Preset objects and define how * offset generators (effect parameters) for their referenced #IpatchSF2Inst. */ #include #include #include #include #include "IpatchSF2PZone.h" #include "IpatchSF2Zone.h" #include "IpatchSF2GenItem.h" #include "IpatchTypeProp.h" #include "ipatch_priv.h" enum { /* generator IDs are used for lower numbers */ PROP_LINK_ITEM = IPATCH_SF2_GEN_ITEM_FIRST_PROP_USER_ID }; static void ipatch_sf2_pzone_gen_item_iface_init (IpatchSF2GenItemIface *genitem_iface); static void ipatch_sf2_pzone_class_init(IpatchSF2PZoneClass *klass); static void ipatch_sf2_pzone_init(IpatchSF2PZone *pzone); static void ipatch_sf2_pzone_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sf2_pzone_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); /* For passing data from class init to gen item interface init */ static GParamSpec **gen_item_specs = NULL; static GParamSpec **gen_item_setspecs = NULL; GType ipatch_sf2_pzone_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchSF2PZoneClass), NULL, NULL, (GClassInitFunc) ipatch_sf2_pzone_class_init, NULL, NULL, sizeof(IpatchSF2PZone), 0, (GInstanceInitFunc) ipatch_sf2_pzone_init, }; static const GInterfaceInfo genitem_iface = { (GInterfaceInitFunc) ipatch_sf2_pzone_gen_item_iface_init, NULL, NULL }; item_type = g_type_register_static(IPATCH_TYPE_SF2_ZONE, "IpatchSF2PZone", &item_info, 0); g_type_add_interface_static(item_type, IPATCH_TYPE_SF2_GEN_ITEM, &genitem_iface); } return (item_type); } /* gen item interface initialization */ static void ipatch_sf2_pzone_gen_item_iface_init(IpatchSF2GenItemIface *genitem_iface) { genitem_iface->genarray_ofs = G_STRUCT_OFFSET(IpatchSF2Zone, genarray); genitem_iface->propstype = IPATCH_SF2_GEN_PROPS_PRESET; g_return_if_fail(gen_item_specs != NULL); g_return_if_fail(gen_item_setspecs != NULL); memcpy(&genitem_iface->specs, gen_item_specs, sizeof(genitem_iface->specs)); memcpy(&genitem_iface->setspecs, gen_item_setspecs, sizeof(genitem_iface->setspecs)); g_free(gen_item_specs); g_free(gen_item_setspecs); } static void ipatch_sf2_pzone_class_init(IpatchSF2PZoneClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->get_property = ipatch_sf2_pzone_get_property; item_class->item_set_property = ipatch_sf2_pzone_set_property; g_object_class_install_property(obj_class, PROP_LINK_ITEM, g_param_spec_object("link-item", _("Link item"), _("Link item"), IPATCH_TYPE_SF2_INST, G_PARAM_READWRITE)); /* install generator properties */ ipatch_sf2_gen_item_iface_install_properties(obj_class, IPATCH_SF2_GEN_PROPS_PRESET, &gen_item_specs, &gen_item_setspecs); } static void ipatch_sf2_pzone_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSF2PZone *pzone = IPATCH_SF2_PZONE(object); IpatchSF2Inst *inst; if(property_id == PROP_LINK_ITEM) { inst = g_value_get_object(value); g_return_if_fail(IPATCH_IS_SF2_INST(inst)); ipatch_sf2_zone_set_link_item_no_notify((IpatchSF2Zone *)pzone, (IpatchItem *)inst, NULL); } else if(!ipatch_sf2_gen_item_iface_set_property((IpatchSF2GenItem *)pzone, property_id, value)) { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_sf2_pzone_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSF2PZone *pzone = IPATCH_SF2_PZONE(object); if(property_id == PROP_LINK_ITEM) { g_value_take_object(value, ipatch_sf2_zone_get_link_item ((IpatchSF2Zone *)pzone)); } else if(!ipatch_sf2_gen_item_iface_get_property((IpatchSF2GenItem *)pzone, property_id, value)) { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_sf2_pzone_init(IpatchSF2PZone *pzone) { ipatch_sf2_gen_array_init(&((IpatchSF2Zone *)pzone)->genarray, TRUE, FALSE); } /** * ipatch_sf2_pzone_new: * * Create a new SoundFont preset zone object. * * Returns: New SoundFont preset zone with a reference count of 1. Caller * owns the reference and removing it will destroy the item, unless another * reference is added (if its parented for example). */ IpatchSF2PZone * ipatch_sf2_pzone_new(void) { return (IPATCH_SF2_PZONE(g_object_new(IPATCH_TYPE_SF2_PZONE, NULL))); } /** * ipatch_sf2_pzone_first: (skip) * @iter: Patch item iterator containing #IpatchSF2PZone items * * Gets the first item in a preset zone iterator. A convenience * wrapper for ipatch_iter_first(). * * Returns: The first preset zone in @iter or %NULL if empty. */ IpatchSF2PZone * ipatch_sf2_pzone_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_SF2_PZONE(obj)); } else { return (NULL); } } /** * ipatch_sf2_pzone_next: (skip) * @iter: Patch item iterator containing #IpatchSF2PZone items * * Gets the next item in a preset zone iterator. A convenience wrapper * for ipatch_iter_next(). * * Returns: The next preset zone in @iter or %NULL if at the end of * the list. */ IpatchSF2PZone * ipatch_sf2_pzone_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_SF2_PZONE(obj)); } else { return (NULL); } } /** * ipatch_sf2_pzone_set_inst: * @pzone: Preset zone to set referenced instrument of * @inst: Instrument to set preset zone's referenced item to * * Sets the referenced instrument of a preset zone. */ void ipatch_sf2_pzone_set_inst(IpatchSF2PZone *pzone, IpatchSF2Inst *inst) { g_return_if_fail(IPATCH_IS_SF2_PZONE(pzone)); g_return_if_fail(IPATCH_IS_SF2_INST(inst)); ipatch_sf2_zone_set_link_item(IPATCH_SF2_ZONE(pzone), IPATCH_ITEM(inst)); } /** * ipatch_sf2_pzone_get_inst: * @pzone: Preset zone to get referenced instrument from * * Gets the referenced instrument from a preset zone. * The returned instrument's reference count is incremented and the caller * is responsible for unrefing it with g_object_unref(). * * Returns: (transfer full): Preset zone's referenced instrument or %NULL if global * zone. Remember to unreference the instrument with g_object_unref() when * done with it. */ IpatchSF2Inst * ipatch_sf2_pzone_get_inst(IpatchSF2PZone *pzone) { IpatchItem *item; g_return_val_if_fail(IPATCH_IS_SF2_PZONE(pzone), NULL); item = ipatch_sf2_zone_get_link_item(IPATCH_SF2_ZONE(pzone)); return (item ? IPATCH_SF2_INST(item) : NULL); } libinstpatch-1.1.6/libinstpatch/IpatchSF2PZone.h000066400000000000000000000044251400263525300215440ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_PZONE_H__ #define __IPATCH_SF2_PZONE_H__ #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchSF2PZone IpatchSF2PZone; typedef struct _IpatchSF2PZoneClass IpatchSF2PZoneClass; #define IPATCH_TYPE_SF2_PZONE (ipatch_sf2_pzone_get_type ()) #define IPATCH_SF2_PZONE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SF2_PZONE, \ IpatchSF2PZone)) #define IPATCH_SF2_PZONE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SF2_PZONE, \ IpatchSF2PZoneClass)) #define IPATCH_IS_SF2_PZONE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SF2_PZONE)) #define IPATCH_IS_SF2_PZONE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SF2_PZONE)) #define IPATCH_SF2_PZONE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SF2_PZONE, \ IpatchSF2PZoneClass)) /* SoundFont pzone item */ struct _IpatchSF2PZone { IpatchSF2Zone parent_instance; }; struct _IpatchSF2PZoneClass { IpatchSF2ZoneClass parent_class; }; GType ipatch_sf2_pzone_get_type(void); IpatchSF2PZone *ipatch_sf2_pzone_new(void); IpatchSF2PZone *ipatch_sf2_pzone_first(IpatchIter *iter); IpatchSF2PZone *ipatch_sf2_pzone_next(IpatchIter *iter); void ipatch_sf2_pzone_set_inst(IpatchSF2PZone *pzone, IpatchSF2Inst *inst); IpatchSF2Inst *ipatch_sf2_pzone_get_inst(IpatchSF2PZone *pzone); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2Preset.c000066400000000000000000000557771400263525300217660ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2Preset * @short_description: SoundFont preset object * @see_also: #IpatchSF2 * @stability: Stable * * SoundFont presets are children of #IpatchSF2 objects and define individual * instruments mapped to MIDI bank/program numbers. */ #include #include #include #include #include "IpatchSF2Preset.h" #include "IpatchSF2PZone.h" #include "IpatchSF2File.h" #include "IpatchSF2GenItem.h" #include "IpatchSF2ModItem.h" #include "IpatchParamProp.h" #include "IpatchTypeProp.h" #include "ipatch_priv.h" /* properties */ enum { /* generator IDs are used for lower numbers */ PROP_TITLE = IPATCH_SF2_GEN_ITEM_FIRST_PROP_USER_ID, PROP_NAME, PROP_BANK, PROP_PROGRAM, PROP_PERCUSSION, PROP_MODULATORS, PROP_LIBRARY, PROP_GENRE, PROP_MORPHOLOGY }; static void ipatch_sf2_preset_class_init(IpatchSF2PresetClass *klass); static void ipatch_sf2_preset_gen_item_iface_init(IpatchSF2GenItemIface *genitem_iface); static void ipatch_sf2_preset_mod_item_iface_init(IpatchSF2ModItemIface *moditem_iface); static void ipatch_sf2_preset_init(IpatchSF2Preset *preset); static void ipatch_sf2_preset_finalize(GObject *gobject); static void ipatch_sf2_preset_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sf2_preset_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sf2_preset_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static const GType *ipatch_sf2_preset_container_child_types(void); static gboolean ipatch_sf2_preset_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type); static void ipatch_sf2_preset_real_set_name(IpatchSF2Preset *preset, const char *name, gboolean name_notify); static gpointer parent_class = NULL; static GType preset_child_types[2] = { 0 }; static GParamSpec *name_pspec, *bank_pspec, *program_pspec, *percuss_pspec; /* For passing data from class init to gen item interface init */ static GParamSpec **gen_item_specs = NULL; static GParamSpec **gen_item_setspecs = NULL; /* For passing between class init and mod item interface init */ static GParamSpec *modulators_spec = NULL; GType ipatch_sf2_preset_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchSF2PresetClass), NULL, NULL, (GClassInitFunc)ipatch_sf2_preset_class_init, NULL, NULL, sizeof(IpatchSF2Preset), 0, (GInstanceInitFunc)ipatch_sf2_preset_init, }; static const GInterfaceInfo genitem_iface = { (GInterfaceInitFunc) ipatch_sf2_preset_gen_item_iface_init, NULL, NULL }; static const GInterfaceInfo moditem_iface = { (GInterfaceInitFunc) ipatch_sf2_preset_mod_item_iface_init, NULL, NULL }; item_type = g_type_register_static(IPATCH_TYPE_CONTAINER, "IpatchSF2Preset", &item_info, 0); g_type_add_interface_static(item_type, IPATCH_TYPE_SF2_GEN_ITEM, &genitem_iface); g_type_add_interface_static(item_type, IPATCH_TYPE_SF2_MOD_ITEM, &moditem_iface); } return (item_type); } static void ipatch_sf2_preset_class_init(IpatchSF2PresetClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->finalize = ipatch_sf2_preset_finalize; obj_class->get_property = ipatch_sf2_preset_get_property; /* we use the IpatchItem item_set_property method */ item_class->item_set_property = ipatch_sf2_preset_set_property; item_class->copy = ipatch_sf2_preset_item_copy; container_class->child_types = ipatch_sf2_preset_container_child_types; container_class->init_iter = ipatch_sf2_preset_container_init_iter; g_object_class_override_property(obj_class, PROP_TITLE, "title"); name_pspec = ipatch_param_set(g_param_spec_string("name", _("Name"), _("Name"), NULL, G_PARAM_READWRITE | IPATCH_PARAM_UNIQUE), "string-max-length", IPATCH_SFONT_NAME_SIZE, /* max len */ NULL); g_object_class_install_property(obj_class, PROP_NAME, name_pspec); /* bank/program are grouped unique (siblings with same bank/program are considered conflicting) */ bank_pspec = g_param_spec_int("bank", _("Bank"), _("MIDI bank number"), 0, 128, 0, G_PARAM_READWRITE | IPATCH_PARAM_UNIQUE); ipatch_param_set(bank_pspec, "unique-group-id", 1, NULL); g_object_class_install_property(obj_class, PROP_BANK, bank_pspec); program_pspec = g_param_spec_int("program", _("Program"), _("MIDI program number"), 0, 127, 0, G_PARAM_READWRITE | IPATCH_PARAM_UNIQUE); ipatch_param_set(program_pspec, "unique-group-id", 1, NULL); g_object_class_install_property(obj_class, PROP_PROGRAM, program_pspec); percuss_pspec = g_param_spec_boolean("percussion", _("Percussion"), _("Percussion preset?"), FALSE, G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_PERCUSSION, percuss_pspec); g_object_class_override_property(obj_class, PROP_MODULATORS, "modulators"); modulators_spec = g_object_class_find_property(obj_class, "modulators"); g_object_class_install_property(obj_class, PROP_LIBRARY, g_param_spec_uint("library", _("Library"), _("Library category"), 0, 0xFFFFFFFF, 0, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_GENRE, g_param_spec_uint("genre", _("Genre"), _("Genre category"), 0, 0xFFFFFFFF, 0, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_MORPHOLOGY, g_param_spec_uint("morphology", _("Morphology"), _("Morphology category"), 0, 0xFFFFFFFF, 0, G_PARAM_READWRITE)); preset_child_types[0] = IPATCH_TYPE_SF2_PZONE; /* install generator properties */ ipatch_sf2_gen_item_iface_install_properties(obj_class, IPATCH_SF2_GEN_PROPS_PRESET_GLOBAL, &gen_item_specs, &gen_item_setspecs); } /* gen item interface initialization */ static void ipatch_sf2_preset_gen_item_iface_init(IpatchSF2GenItemIface *genitem_iface) { genitem_iface->genarray_ofs = G_STRUCT_OFFSET(IpatchSF2Preset, genarray); genitem_iface->propstype = IPATCH_SF2_GEN_PROPS_PRESET_GLOBAL; g_return_if_fail(gen_item_specs != NULL); g_return_if_fail(gen_item_setspecs != NULL); memcpy(&genitem_iface->specs, gen_item_specs, sizeof(genitem_iface->specs)); memcpy(&genitem_iface->setspecs, gen_item_setspecs, sizeof(genitem_iface->setspecs)); g_free(gen_item_specs); g_free(gen_item_setspecs); } /* mod item interface initialization */ static void ipatch_sf2_preset_mod_item_iface_init(IpatchSF2ModItemIface *moditem_iface) { moditem_iface->modlist_ofs = G_STRUCT_OFFSET(IpatchSF2Preset, mods); /* cache the modulators property for fast notifications */ moditem_iface->mod_pspec = modulators_spec; } static void ipatch_sf2_preset_init(IpatchSF2Preset *preset) { ipatch_sf2_gen_array_init(&preset->genarray, TRUE, FALSE); } static void ipatch_sf2_preset_finalize(GObject *gobject) { IpatchSF2Preset *preset = IPATCH_SF2_PRESET(gobject); /* nothing should reference the preset after this, but we set pointers to NULL to help catch invalid references. Locking of preset is required since in reality all its children do still hold references */ IPATCH_ITEM_WLOCK(preset); g_free(preset->name); preset->name = NULL; ipatch_sf2_mod_list_free(preset->mods, TRUE); preset->mods = NULL; IPATCH_ITEM_WUNLOCK(preset); if(G_OBJECT_CLASS(parent_class)->finalize) { G_OBJECT_CLASS(parent_class)->finalize(gobject); } } static void ipatch_sf2_preset_get_title(IpatchSF2Preset *preset, GValue *value) { int bank, program; char *name, *s; g_object_get(preset, "bank", &bank, "program", &program, "name", &name, NULL); s = g_strdup_printf("%03d-%03d %s", bank, program, name); g_free(name); g_value_take_string(value, s); } static void ipatch_sf2_preset_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSF2Preset *preset = IPATCH_SF2_PRESET(object); IpatchSF2ModList *list; GValue oldvalue = { 0 }, newvalue = { 0 }; int newbank, oldbank; gboolean newpercuss, oldpercuss; /* generator property? */ if(ipatch_sf2_gen_item_iface_set_property((IpatchSF2GenItem *)preset, property_id, value)) { return; } switch(property_id) { case PROP_NAME: ipatch_sf2_preset_real_set_name(preset, g_value_get_string(value), FALSE); break; case PROP_BANK: newbank = g_value_get_int(value); IPATCH_ITEM_WLOCK(preset); oldbank = preset->bank; preset->bank = newbank; IPATCH_ITEM_WUNLOCK(preset); /* do "percussion" property notify if necessary */ if((newbank == 128) != (oldbank == 128)) { g_value_init(&newvalue, G_TYPE_BOOLEAN); g_value_init(&oldvalue, G_TYPE_BOOLEAN); g_value_set_boolean(&newvalue, newbank == 128); g_value_set_boolean(&oldvalue, oldbank == 128); ipatch_item_prop_notify((IpatchItem *)preset, percuss_pspec, &newvalue, &oldvalue); g_value_unset(&newvalue); g_value_unset(&oldvalue); } break; case PROP_PROGRAM: IPATCH_ITEM_WLOCK(preset); preset->program = g_value_get_int(value); IPATCH_ITEM_WUNLOCK(preset); break; case PROP_PERCUSSION: newpercuss = g_value_get_boolean(value); IPATCH_ITEM_WLOCK(preset); oldbank = preset->bank; oldpercuss = (preset->bank == 128); if(newpercuss != oldpercuss) { preset->bank = newpercuss ? 128 : 0; } IPATCH_ITEM_WUNLOCK(preset); /* do "bank" property notify if necessary */ if(newpercuss != oldpercuss) { g_value_init(&newvalue, G_TYPE_INT); g_value_init(&oldvalue, G_TYPE_INT); g_value_set_int(&newvalue, newpercuss ? 128 : 0); g_value_set_int(&oldvalue, oldbank); ipatch_item_prop_notify((IpatchItem *)preset, bank_pspec, &newvalue, &oldvalue); g_value_unset(&newvalue); g_value_unset(&oldvalue); } break; case PROP_MODULATORS: list = (IpatchSF2ModList *)g_value_get_boxed(value); ipatch_sf2_mod_item_set_mods(IPATCH_SF2_MOD_ITEM(preset), list, IPATCH_SF2_MOD_NO_NOTIFY); break; case PROP_LIBRARY: IPATCH_ITEM_WLOCK(preset); preset->library = g_value_get_uint(value); IPATCH_ITEM_WUNLOCK(preset); break; case PROP_GENRE: IPATCH_ITEM_WLOCK(preset); preset->genre = g_value_get_uint(value); IPATCH_ITEM_WUNLOCK(preset); break; case PROP_MORPHOLOGY: IPATCH_ITEM_WLOCK(preset); preset->morphology = g_value_get_uint(value); IPATCH_ITEM_WUNLOCK(preset); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } /* need to do title notify? */ if(property_id == PROP_NAME || property_id == PROP_BANK || property_id == PROP_PROGRAM || property_id == PROP_PERCUSSION) { GValue titleval = { 0 }; g_value_init(&titleval, G_TYPE_STRING); ipatch_sf2_preset_get_title(preset, &titleval); ipatch_item_prop_notify((IpatchItem *)preset, ipatch_item_pspec_title, &titleval, NULL); g_value_unset(&titleval); } } static void ipatch_sf2_preset_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSF2Preset *preset = IPATCH_SF2_PRESET(object); IpatchSF2ModList *list; /* generator property? */ if(ipatch_sf2_gen_item_iface_get_property((IpatchSF2GenItem *)preset, property_id, value)) { return; } switch(property_id) { case PROP_TITLE: ipatch_sf2_preset_get_title(preset, value); break; case PROP_NAME: g_value_take_string(value, ipatch_sf2_preset_get_name(preset)); break; case PROP_BANK: g_value_set_int(value, preset->bank); break; case PROP_PROGRAM: g_value_set_int(value, preset->program); break; case PROP_PERCUSSION: g_value_set_boolean(value, (preset->bank == 128) ? TRUE : FALSE); break; case PROP_MODULATORS: list = ipatch_sf2_mod_item_get_mods(IPATCH_SF2_MOD_ITEM(preset)); g_value_take_boxed(value, list); break; case PROP_LIBRARY: g_value_set_uint(value, preset->library); break; case PROP_GENRE: g_value_set_uint(value, preset->genre); break; case PROP_MORPHOLOGY: g_value_set_uint(value, preset->morphology); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_sf2_preset_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchSF2Preset *src_pset, *dest_pset; IpatchItem *zitem; GSList *p; src_pset = IPATCH_SF2_PRESET(src); dest_pset = IPATCH_SF2_PRESET(dest); IPATCH_ITEM_RLOCK(src_pset); dest_pset->name = g_strdup(src_pset->name); dest_pset->program = src_pset->program; dest_pset->bank = src_pset->bank; dest_pset->library = src_pset->library; dest_pset->genre = src_pset->genre; dest_pset->morphology = src_pset->morphology; dest_pset->genarray = src_pset->genarray; dest_pset->mods = ipatch_sf2_mod_list_duplicate(src_pset->mods); p = src_pset->zones; while(p) { zitem = ipatch_item_duplicate_link_func(IPATCH_ITEM(p->data), link_func, user_data); dest_pset->zones = g_slist_prepend(dest_pset->zones, zitem); ipatch_item_set_parent(zitem, IPATCH_ITEM(dest_pset)); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(src_pset); dest_pset->zones = g_slist_reverse(dest_pset->zones); } static const GType * ipatch_sf2_preset_container_child_types(void) { return (preset_child_types); } /* container is locked by caller */ static gboolean ipatch_sf2_preset_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type) { IpatchSF2Preset *preset = IPATCH_SF2_PRESET(container); if(!g_type_is_a(type, IPATCH_TYPE_SF2_PZONE)) { g_critical("Invalid child type '%s' for parent of type '%s'", g_type_name(type), g_type_name(G_OBJECT_TYPE(container))); return (FALSE); } ipatch_iter_GSList_init(iter, &preset->zones); return (TRUE); } /** * ipatch_sf2_preset_new: * * Create a new SoundFont preset object. * * Returns: New SoundFont preset with a reference count of 1. Caller * owns the reference and removing it will destroy the item, unless another * reference is added (if its parented for example). */ IpatchSF2Preset * ipatch_sf2_preset_new(void) { return (IPATCH_SF2_PRESET(g_object_new(IPATCH_TYPE_SF2_PRESET, NULL))); } /** * ipatch_sf2_preset_first: (skip) * @iter: Patch item iterator containing #IpatchSF2Preset items * * Gets the first item in a preset iterator. A convenience wrapper for * ipatch_iter_first(). * * Returns: The first preset in @iter or %NULL if empty. */ IpatchSF2Preset * ipatch_sf2_preset_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_SF2_PRESET(obj)); } else { return (NULL); } } /** * ipatch_sf2_preset_next: (skip) * @iter: Patch item iterator containing #IpatchSF2Preset items * * Gets the next item in a preset iterator. A convenience wrapper for * ipatch_iter_next(). * * Returns: The next preset in @iter or %NULL if at the end of the list. */ IpatchSF2Preset * ipatch_sf2_preset_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_SF2_PRESET(obj)); } else { return (NULL); } } /** * ipatch_sf2_preset_new_zone: * @preset: SoundFont preset * @inst: Referenced instrument for new zone * * A convenience function for quickly creating a new preset zone, adding it * to @preset and setting the zone's referenced instrument to @inst. */ void ipatch_sf2_preset_new_zone(IpatchSF2Preset *preset, IpatchSF2Inst *inst) { IpatchSF2PZone *pzone; g_return_if_fail(IPATCH_IS_SF2_PRESET(preset)); g_return_if_fail(IPATCH_IS_SF2_INST(inst)); pzone = ipatch_sf2_pzone_new(); /* ++ ref new zone */ ipatch_sf2_zone_set_link_item(IPATCH_SF2_ZONE(pzone), IPATCH_ITEM(inst)); ipatch_container_append(IPATCH_CONTAINER(preset), IPATCH_ITEM(pzone)); g_object_unref(pzone); /* -- unref preset zone */ } /** * ipatch_sf2_preset_set_name: * @preset: Preset to set name of * @name: (nullable): Value to set name to * * Sets the name of a SoundFont preset. */ void ipatch_sf2_preset_set_name(IpatchSF2Preset *preset, const char *name) { g_return_if_fail(IPATCH_IS_SF2_PRESET(preset)); ipatch_sf2_preset_real_set_name(preset, name, TRUE); } /* the real preset name set routine */ static void ipatch_sf2_preset_real_set_name(IpatchSF2Preset *preset, const char *name, gboolean name_notify) { GValue oldname = { 0 }, newname = { 0 }; char *newstr, *oldstr; newstr = g_strdup(name); IPATCH_ITEM_WLOCK(preset); oldstr = preset->name; preset->name = newstr; IPATCH_ITEM_WUNLOCK(preset); g_value_init(&oldname, G_TYPE_STRING); g_value_take_string(&oldname, oldstr); g_value_init(&newname, G_TYPE_STRING); g_value_set_static_string(&newname, name); if(name_notify) ipatch_item_prop_notify((IpatchItem *)preset, name_pspec, &newname, &oldname); ipatch_item_prop_notify((IpatchItem *)preset, ipatch_item_pspec_title, &newname, &oldname); g_value_unset(&newname); g_value_unset(&oldname); } /** * ipatch_sf2_preset_get_name: * @preset: Preset to get name of * * Gets the name of a SoundFont preset. * * Returns: Name of preset or %NULL if not set. String value should be freed * when finished with it. */ char * ipatch_sf2_preset_get_name(IpatchSF2Preset *preset) { char *name = NULL; g_return_val_if_fail(IPATCH_IS_SF2_PRESET(preset), NULL); IPATCH_ITEM_RLOCK(preset); if(preset->name) { name = g_strdup(preset->name); } IPATCH_ITEM_RUNLOCK(preset); return (name); } /** * ipatch_sf2_preset_set_midi_locale: * @preset: Preset to set MIDI locale of * @bank: MIDI bank number to assign to preset * @program: MIDI program number to assign to preset * * Sets the MIDI locale of a preset (bank and program numbers). */ void ipatch_sf2_preset_set_midi_locale(IpatchSF2Preset *preset, int bank, int program) { g_object_set(preset, "bank", bank, "program", program, NULL); } /** * ipatch_sf2_preset_get_midi_locale: * @preset: Preset to get MIDI locale from * @bank: Location to store preset's MIDI bank number or %NULL * @program: Location to store preset's MIDI program number or %NULL * * Gets the MIDI locale of a SoundFont preset (bank and program numbers). */ void ipatch_sf2_preset_get_midi_locale(IpatchSF2Preset *preset, int *bank, int *program) { g_return_if_fail(IPATCH_IS_SF2_PRESET(preset)); IPATCH_ITEM_RLOCK(preset); if(bank) { *bank = preset->bank; } if(program) { *program = preset->program; } IPATCH_ITEM_RUNLOCK(preset); } /** * ipatch_sf2_preset_compare: * @p1: First preset in comparison * @p2: Second preset in comparison * * Preset comparison function for sorting. Compare two presets by their * MIDI bank:program numbers. Note that this function is compatible with * GCompareFunc and can therefore be used with g_list_sort, etc. * * Returns: Comparison result that is less than, equal to, or greater than zero * if @p1 is found, respectively, to be less than, to match, or be greater * than @p2. */ int ipatch_sf2_preset_compare(const IpatchSF2Preset *p1, const IpatchSF2Preset *p2) { gint32 aval, bval; aval = ((gint32)(p1->bank) << 16) | p1->program; bval = ((gint32)(p2->bank) << 16) | p2->program; return (aval - bval); } libinstpatch-1.1.6/libinstpatch/IpatchSF2Preset.h000066400000000000000000000065661400263525300217630ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_PRESET_H__ #define __IPATCH_SF2_PRESET_H__ #include #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchSF2Preset IpatchSF2Preset; typedef struct _IpatchSF2PresetClass IpatchSF2PresetClass; #define IPATCH_TYPE_SF2_PRESET (ipatch_sf2_preset_get_type ()) #define IPATCH_SF2_PRESET(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SF2_PRESET, \ IpatchSF2Preset)) #define IPATCH_SF2_PRESET_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SF2_PRESET, \ IpatchSF2PresetClass)) #define IPATCH_IS_SF2_PRESET(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SF2_PRESET)) #define IPATCH_IS_SF2_PRESET_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SF2_PRESET)) #define IPATCH_SF2_PRESET_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SF2_PRESET, \ IpatchSF2PresetClass)) /* SoundFont preset item */ struct _IpatchSF2Preset { IpatchContainer parent_instance; char *name; /* name of preset */ guint16 program; /* MIDI preset map number */ guint16 bank; /* MIDI bank map number */ GSList *zones; /* list of preset zones */ GSList *mods; /* modulators for global zone */ IpatchSF2GenArray genarray; /* generator array for global zone */ guint32 library; /* Not used (preserved) */ guint32 genre; /* Not used (preserved) */ guint32 morphology; /* Not used (preserved) */ }; struct _IpatchSF2PresetClass { IpatchContainerClass parent_class; }; GType ipatch_sf2_preset_get_type(void); IpatchSF2Preset *ipatch_sf2_preset_new(void); #define ipatch_sf2_preset_get_zones(preset) \ ipatch_container_get_children (IPATCH_CONTAINER (preset), \ IPATCH_TYPE_SF2_ZONE) IpatchSF2Preset *ipatch_sf2_preset_first(IpatchIter *iter); IpatchSF2Preset *ipatch_sf2_preset_next(IpatchIter *iter); void ipatch_sf2_preset_new_zone(IpatchSF2Preset *preset, IpatchSF2Inst *inst); void ipatch_sf2_preset_set_name(IpatchSF2Preset *preset, const char *name); char *ipatch_sf2_preset_get_name(IpatchSF2Preset *preset); void ipatch_sf2_preset_set_midi_locale(IpatchSF2Preset *preset, int bank, int program); void ipatch_sf2_preset_get_midi_locale(IpatchSF2Preset *preset, int *bank, int *program); int ipatch_sf2_preset_compare(const IpatchSF2Preset *p1, const IpatchSF2Preset *p2); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2Reader.c000066400000000000000000001501271400263525300217070ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2Reader * @short_description: SoundFont file reader * @see_also: * @stability: Stable * * Reads a SoundFont file and loads it into a object tree (#IpatchSF2). */ #include #include #include #include #include #include #include "IpatchSF2Reader.h" #include "IpatchSF2File.h" #include "IpatchSF2File_priv.h" #include "IpatchSF2ModItem.h" #include "IpatchGig.h" #include "IpatchGigEffects.h" #include "IpatchGigFile.h" #include "IpatchSampleStore.h" #include "IpatchSampleStoreRam.h" #include "IpatchSampleStoreRom.h" #include "IpatchSampleStoreSplit24.h" #include "IpatchGigInst.h" #include "IpatchGigRegion.h" #include "IpatchSampleStoreFile.h" #include "IpatchUnit.h" #include "ipatch_priv.h" #include "i18n.h" /* ----- ** WARNING ** ----- We don't care about locking in here, because we are the exclusive owner of the loading SoundFont. Many fields are accessed directly, etc. This is not an example of proper use of libInstPatch.. Don't try this at home.. Blah blah. */ static void ipatch_sf2_reader_class_init(IpatchSF2ReaderClass *klass); static void ipatch_sf2_reader_init(IpatchSF2Reader *reader); static void ipatch_sf2_reader_finalize(GObject *object); static void ipatch_sf2_load_phdr(IpatchFileHandle *handle, IpatchSF2Phdr *phdr); static void ipatch_sf2_load_ihdr(IpatchFileHandle *handle, IpatchSF2Ihdr *ihdr); static void ipatch_sf2_load_shdr(IpatchFileHandle *handle, IpatchSF2Shdr *shdr); static void ipatch_sf2_load_mod(IpatchFileHandle *handle, IpatchSF2Mod *mod); static void ipatch_sf2_load_gen(IpatchFileHandle *handle, int *genid, IpatchSF2GenAmount *amount); static gboolean ipatch_sf2_load_level_0(IpatchSF2Reader *reader, GError **err); static gboolean sfload_infos(IpatchSF2Reader *reader, GError **err); static gboolean sfload_phdrs(IpatchSF2Reader *reader, GError **err); static gboolean sfload_pbags(IpatchSF2Reader *reader, GError **err); static gboolean sfload_pmods(IpatchSF2Reader *reader, GError **err); static gboolean sfload_pgens(IpatchSF2Reader *reader, GError **err); static gboolean sfload_ihdrs(IpatchSF2Reader *reader, GError **err); static gboolean sfload_ibags(IpatchSF2Reader *reader, GError **err); static gboolean sfload_imods(IpatchSF2Reader *reader, GError **err); static gboolean sfload_igens(IpatchSF2Reader *reader, GError **err); static gboolean sfload_shdrs(IpatchSF2Reader *reader, GError **err); #define SFONT_ERROR_MSG "SoundFont reader error: %s" #define SET_SIZE_ERROR(riff, level, err) \ g_set_error (err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_SIZE_MISMATCH,\ _(SFONT_ERROR_MSG), \ ipatch_riff_message_detail(riff, -1, "Unexpected chunk size")) #define SET_DATA_ERROR(riff, level, err) \ g_set_error (err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_INVALID_DATA,\ _(SFONT_ERROR_MSG), \ ipatch_riff_message_detail (riff, -1, "Invalid data")) G_DEFINE_TYPE(IpatchSF2Reader, ipatch_sf2_reader, IPATCH_TYPE_RIFF) static void ipatch_sf2_reader_class_init(IpatchSF2ReaderClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->finalize = ipatch_sf2_reader_finalize; } static void ipatch_sf2_reader_init(IpatchSF2Reader *reader) { reader->sf = NULL; } static void ipatch_sf2_reader_finalize(GObject *object) { IpatchSF2Reader *reader = IPATCH_SF2_READER(object); if(reader->sf) { g_object_unref(reader->sf); /* -- unref SoundFont */ reader->sf = NULL; } g_free(reader->pbag_table); reader->pbag_table = NULL; g_free(reader->ibag_table); reader->ibag_table = NULL; g_free(reader->inst_table); reader->inst_table = NULL; g_free(reader->sample_table); reader->sample_table = NULL; if(G_OBJECT_CLASS(ipatch_sf2_reader_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_sf2_reader_parent_class)->finalize(object); } } /** * ipatch_sf2_reader_new: * @handle: SoundFont 2 file handle to parse or %NULL to set later * * Create a new SoundFont file reader * * Returns: The new SoundFont file reader */ IpatchSF2Reader * ipatch_sf2_reader_new(IpatchFileHandle *handle) { IpatchSF2Reader *reader; g_return_val_if_fail(!handle || IPATCH_IS_SF2_FILE(handle->file), NULL); reader = g_object_new(IPATCH_TYPE_SF2_READER, NULL); if(handle) { ipatch_sf2_reader_set_file_handle(reader, handle); } return (reader); } /** * ipatch_sf2_reader_set_file_handle: * @reader: SoundFont reader object * @handle: SoundFont 2 file handle * * Set the SoundFont file handle of a SoundFont reader. A convenience * function, since ipatch_riff_set_file_handle() could also be used, albeit * without stricter type casting. */ void ipatch_sf2_reader_set_file_handle(IpatchSF2Reader *reader, IpatchFileHandle *handle) { g_return_if_fail(IPATCH_IS_SF2_READER(reader)); g_return_if_fail(handle && IPATCH_IS_SF2_FILE(handle->file)); ipatch_riff_set_file_handle(IPATCH_RIFF(reader), handle); } /** * ipatch_sf2_reader_load: * @reader: SF2 reader object * @err: Location to store error info or %NULL * * Load an SF2 file. * * Returns: (transfer full): New SF2 object with refcount of 1. */ IpatchSF2 * ipatch_sf2_reader_load(IpatchSF2Reader *reader, GError **err) { IpatchRiff *riff; IpatchRiffChunk *chunk; GError *local_err = NULL; int size; g_return_val_if_fail(IPATCH_IS_SF2_READER(reader), NULL); riff = IPATCH_RIFF(reader); g_return_val_if_fail(riff->handle && IPATCH_IS_SF2_FILE(riff->handle->file), NULL); /* start parsing */ if(!(chunk = ipatch_riff_start_read(riff, err))) { return (NULL); } if(chunk->id != IPATCH_SFONT_FOURCC_SFBK) { g_set_error(err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_UNEXPECTED_ID, _("Not a SoundFont file (RIFF id = '%4s')"), chunk->idstr); return (NULL); } /* verify total size of file with RIFF chunk size */ size = ipatch_file_get_size(riff->handle->file, &local_err); if(size == -1) { g_warning("SoundFont file size check failed: %s", ipatch_gerror_message(local_err)); g_clear_error(&local_err); } else if(size != chunk->size + IPATCH_RIFF_HEADER_SIZE) { g_set_error(err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_SIZE_MISMATCH, _("File size mismatch (chunk size = %d, actual = %d)"), chunk->size + IPATCH_RIFF_HEADER_SIZE, size); return (NULL); } reader->sf = ipatch_sf2_new(); /* ++ ref new object */ ipatch_sf2_set_file(reader->sf, IPATCH_SF2_FILE(riff->handle->file)); if(!ipatch_sf2_load_level_0(reader, err)) { goto err; } ipatch_item_clear_flags(IPATCH_ITEM(reader->sf), IPATCH_BASE_SAVED | IPATCH_BASE_CHANGED); /* ++ ref for caller, finalize() will remove reader's reference */ return (g_object_ref(reader->sf)); err: g_object_unref(reader->sf); reader->sf = NULL; return (NULL); } /** * ipatch_sf2_load_phdr: (skip) * @handle: File handle containing buffered data * @phdr: Pointer to a user supplied preset header structure * * Parses a raw preset header in file @handle with buffered data. */ static void ipatch_sf2_load_phdr(IpatchFileHandle *handle, IpatchSF2Phdr *phdr) { g_return_if_fail(handle != NULL); g_return_if_fail(phdr != NULL); ipatch_file_buf_read(handle, &phdr->name, IPATCH_SFONT_NAME_SIZE); phdr->program = ipatch_file_buf_read_u16(handle); phdr->bank = ipatch_file_buf_read_u16(handle); phdr->bag_index = ipatch_file_buf_read_u16(handle); phdr->library = ipatch_file_buf_read_u32(handle); phdr->genre = ipatch_file_buf_read_u32(handle); phdr->morphology = ipatch_file_buf_read_u32(handle); } /** * ipatch_sf2_load_ihdr: (skip) * @handle: File handle containing buffered data * @ihdr: Pointer to a user supplied instrument header structure * * Parses a raw instrument header in file @handle with buffered data. */ static void ipatch_sf2_load_ihdr(IpatchFileHandle *handle, IpatchSF2Ihdr *ihdr) { g_return_if_fail(handle != NULL); g_return_if_fail(ihdr != NULL); ipatch_file_buf_read(handle, &ihdr->name, IPATCH_SFONT_NAME_SIZE); ihdr->bag_index = ipatch_file_buf_read_u16(handle); } /** * ipatch_sf2_load_shdr: (skip) * @handle: File handle containing buffered data * @shdr: Pointer to a user supplied sample header structure * * Parses a raw sample header in file @handle with buffered data. */ static void ipatch_sf2_load_shdr(IpatchFileHandle *handle, IpatchSF2Shdr *shdr) { g_return_if_fail(handle != NULL); g_return_if_fail(shdr != NULL); ipatch_file_buf_read(handle, &shdr->name, IPATCH_SFONT_NAME_SIZE); shdr->start = ipatch_file_buf_read_u32(handle); shdr->end = ipatch_file_buf_read_u32(handle); shdr->loop_start = ipatch_file_buf_read_u32(handle); shdr->loop_end = ipatch_file_buf_read_u32(handle); shdr->rate = ipatch_file_buf_read_u32(handle); shdr->root_note = ipatch_file_buf_read_u8(handle); shdr->fine_tune = ipatch_file_buf_read_u8(handle); shdr->link_index = ipatch_file_buf_read_u16(handle); shdr->type = ipatch_file_buf_read_u16(handle); } /** * ipatch_sf2_load_mod: (skip) * @handle: File handle containing buffered data * @mod: Pointer to a user supplied modulator structure * * Parses a raw modulator in file @handle with buffered data. */ static void ipatch_sf2_load_mod(IpatchFileHandle *handle, IpatchSF2Mod *mod) { g_return_if_fail(handle != NULL); g_return_if_fail(mod != NULL); mod->src = ipatch_file_buf_read_u16(handle); mod->dest = ipatch_file_buf_read_u16(handle); mod->amount = ipatch_file_buf_read_u16(handle); mod->amtsrc = ipatch_file_buf_read_u16(handle); mod->trans = ipatch_file_buf_read_u16(handle); } /** * ipatch_sf2_load_gen: (skip) * @handle: File handle containing buffered data * @genid: Pointer to store the generator ID in * @amount: Pointer to a generator amount to store the amount in * * Parses a raw generator in file @handle with buffered data. */ static void ipatch_sf2_load_gen(IpatchFileHandle *handle, int *genid, IpatchSF2GenAmount *amount) { g_return_if_fail(handle != NULL); g_return_if_fail(genid != NULL); g_return_if_fail(amount != NULL); *genid = ipatch_file_buf_read_u16(handle); /* check if genid is valid (preset or inst) and is a range unit */ if(ipatch_sf2_gen_is_valid(*genid, FALSE) && ipatch_sf2_gen_info[*genid].unit == IPATCH_UNIT_TYPE_RANGE) { /* load the range */ amount->range.low = ipatch_file_buf_read_u8(handle); amount->range.high = ipatch_file_buf_read_u8(handle); } else { amount->sword = ipatch_file_buf_read_s16(handle); } } static gboolean ipatch_sf2_load_level_0(IpatchSF2Reader *reader, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchRiffChunk *chunk; IpatchSF2File *sfont_file; /* load INFO LIST chunk */ if(!ipatch_riff_read_chunk_verify(riff, IPATCH_RIFF_CHUNK_LIST, IPATCH_SFONT_FOURCC_INFO, err)) { return (FALSE); } if(!sfload_infos(reader, err)) { return (FALSE); } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } /* load SDTA LIST chunk */ if(!ipatch_riff_read_chunk_verify(riff, IPATCH_RIFF_CHUNK_LIST, IPATCH_SFONT_FOURCC_SDTA, err)) { return (FALSE); } /* initialize sample positions to 0 so we know if they get set or not */ sfont_file = IPATCH_SF2_FILE(IPATCH_BASE(reader->sf)->file); sfont_file->sample_pos = 0; sfont_file->sample24_pos = 0; /* smpl chunk is theoretically optional if all samples are ROM samples, but * these days thats a pretty useless SoundFont, so we assume SMPL exists. */ if(!(chunk = ipatch_riff_read_chunk_verify(riff, IPATCH_RIFF_CHUNK_SUB, IPATCH_SFONT_FOURCC_SMPL, err))) { return (FALSE); } /* store offset into file of sample data (if any) */ if(chunk->size > 0) { sfont_file->sample_pos = ipatch_riff_get_position(riff); sfont_file->sample_size = chunk->size / 2; /* save size (in samples) */ } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); /* */ } chunk = ipatch_riff_read_chunk(riff, err); /* check for "sm24" sample chunk (LS bytes of 24 bit audio) */ if(chunk && chunk->type == IPATCH_RIFF_CHUNK_SUB && chunk->id == IPATCH_SFONT_FOURCC_SM24) { /* verify sm24 is equal to the number of samples in */ if(chunk->size == sfont_file->sample_size || chunk->size == sfont_file->sample_size + (sfont_file->sample_size & 1)) { /* set 24 bit samples flag and store offset of chunk */ ipatch_item_set_flags(reader->sf, IPATCH_SF2_SAMPLES_24BIT); sfont_file->sample24_pos = ipatch_riff_get_position(riff); } else { g_critical("Invalid size for SoundFont sample 24 chunk, ignoring"); } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); /* */ } } else if(!ipatch_riff_get_error(riff, NULL)) { return (FALSE); } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); /* */ } /* load PDTA LIST chunk */ if(!ipatch_riff_read_chunk_verify(riff, IPATCH_RIFF_CHUNK_LIST, IPATCH_SFONT_FOURCC_PDTA, err)) { return (FALSE); } if(!sfload_phdrs(reader, err)) { return (FALSE); } /* we load all presets/instruments/samples first so no fixup is required later for numeric indexes. Save RIFF state here so we can return. */ ipatch_riff_push_state(riff); if(!ipatch_riff_skip_chunks(riff, 3, err)) /* skip pbags pmods pgens */ { return (FALSE); } if(!sfload_ihdrs(reader, err)) { return (FALSE); } if(!ipatch_riff_skip_chunks(riff, 3, err)) /* skip ibags imods igens */ { return (FALSE); } if(!sfload_shdrs(reader, err)) { return (FALSE); } /* return to preset bag chunk */ if(!ipatch_riff_pop_state(riff, err)) { return (FALSE); } if(!sfload_pbags(reader, err)) { return (FALSE); } if(!sfload_pmods(reader, err)) { return (FALSE); } if(!sfload_pgens(reader, err)) { return (FALSE); } /* skip ihdrs (already loaded above) */ if(!ipatch_riff_skip_chunks(riff, 1, err)) { return (FALSE); } if(!sfload_ibags(reader, err)) { return (FALSE); } if(!sfload_imods(reader, err)) { return (FALSE); } if(!sfload_igens(reader, err)) { return (FALSE); } return (TRUE); } static gboolean sfload_infos(IpatchSF2Reader *reader, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchRiffChunk *chunk; /* loop over info chunks */ while((chunk = ipatch_riff_read_chunk(riff, err))) { if(chunk->type == IPATCH_RIFF_CHUNK_SUB) { if(chunk->id == IPATCH_SFONT_FOURCC_IFIL) /* version chunk? */ { if(!ipatch_file_buf_load(riff->handle, IPATCH_SFONT_VERSION_SIZE, err)) { return (FALSE); } reader->sf->ver_major = ipatch_file_buf_read_u16(riff->handle); reader->sf->ver_minor = ipatch_file_buf_read_u16(riff->handle); if(reader->sf->ver_major != 2) { g_critical(_("SoundFont version is %d.%02d which" " is not supported"), reader->sf->ver_major, reader->sf->ver_minor); return (FALSE); } if(reader->sf->ver_minor > 4) g_warning(_("SoundFont version is newer than 2.04," " some information might be uneditable")); } else if(chunk->id == IPATCH_SFONT_FOURCC_IVER) /* ROM version? */ { if(!ipatch_file_buf_load(riff->handle, IPATCH_SFONT_VERSION_SIZE, err)) { return (FALSE); } reader->sf->romver_major = ipatch_file_buf_read_u16(riff->handle); reader->sf->romver_minor = ipatch_file_buf_read_u16(riff->handle); } else if(ipatch_sf2_info_id_is_valid(chunk->id)) /* regular string based info chunk */ { guint32 maxsize, size; char *s; if(chunk->size > 0) { maxsize = ipatch_sf2_get_info_max_size(chunk->id); if(chunk->size > maxsize) { g_warning(_("Invalid size %d for INFO chunk \"%.4s\""), chunk->size, chunk->idstr); size = maxsize; } else { size = chunk->size; } s = g_malloc(size); /* alloc for info string */ if(!ipatch_file_read(riff->handle, s, size, err)) { g_free(s); return (FALSE); } s[size - 1] = '\0'; /* force terminate info string */ ipatch_sf2_set_info(reader->sf, chunk->id, s); g_free(s); } } else { g_warning(_("Unknown INFO chunk \"%.4s\""), chunk->idstr); } } /* chunk->type == IPATCH_RIFF_CHUNK_SUB */ if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } } /* while () */ return (ipatch_riff_get_error(riff, NULL)); } /* preset header reader */ static gboolean sfload_phdrs(IpatchSF2Reader *reader, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchSF2Preset *preset = NULL, *prev = NULL; /* current & previous preset item */ IpatchIter preset_iter, zone_iter; IpatchItem *zone; IpatchRiffChunk *chunk; IpatchSF2Phdr phdr; guint16 zndx = 0, pzndx = 0; int i, i2; if(!ipatch_riff_read_chunk_verify(riff, IPATCH_RIFF_CHUNK_SUB, IPATCH_SFONT_FOURCC_PHDR, err)) { return (FALSE); } chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size == 0) { return (TRUE); /* no preset headers? */ } if(chunk->size % IPATCH_SFONT_PHDR_SIZE) /* verify chunk size */ { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } /* initialize iterator to SoundFont preset list */ if(!ipatch_container_init_iter(IPATCH_CONTAINER(reader->sf), &preset_iter, IPATCH_TYPE_SF2_PRESET)) { return (FALSE); } /* loop over all preset headers (including dummy terminal record) */ i = chunk->size / IPATCH_SFONT_PHDR_SIZE; for(; i > 0; i--) { if(!ipatch_file_buf_load(riff->handle, IPATCH_SFONT_PHDR_SIZE, err)) { return (FALSE); } ipatch_sf2_load_phdr(riff->handle, &phdr); zndx = phdr.bag_index; if(i != 1) /* don't add terminal record */ { preset = ipatch_sf2_preset_new(); /* ++ ref new preset */ preset->name = g_strndup(phdr.name, 20); preset->program = phdr.program; preset->bank = phdr.bank; preset->library = phdr.library; preset->genre = phdr.genre; preset->morphology = phdr.morphology; /* by default insert_iter keeps appending */ ipatch_container_insert_iter((IpatchContainer *)(reader->sf), (IpatchItem *)preset, &preset_iter); g_object_unref(preset); /* -- unref new preset */ } if(prev) /* not first preset? */ { if(zndx < pzndx) /* make sure zone index isn't decreasing */ { g_set_error(err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_INVALID_DATA, "Invalid preset zone index"); return (FALSE); } /* init iterator to list of zones */ if(!ipatch_container_init_iter((IpatchContainer *)prev, &zone_iter, IPATCH_TYPE_SF2_PZONE)) { return (FALSE); } i2 = zndx - pzndx; /* # of zones in last preset */ while(i2--) /* create zones for last preset */ { /* ++ ref new zone and insert it */ zone = g_object_new(IPATCH_TYPE_SF2_PZONE, NULL); ipatch_container_insert_iter((IpatchContainer *)prev, zone, &zone_iter); g_object_unref(zone); /* -- unref new zone */ } } else if(zndx > 0) /* 1st preset, warn if ofs > 0 */ { g_warning(_("%d preset zones not referenced, discarding"), zndx); } prev = preset; /* update previous preset ptr */ pzndx = zndx; } reader->pbag_count = zndx; /* total number of preset zones */ if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } return (TRUE); } /* preset bag reader */ static gboolean sfload_pbags(IpatchSF2Reader *reader, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchRiffChunk *chunk; guint16 *bag_table; guint16 genndx, modndx; guint16 pgenndx, pmodndx; guint i; if(!ipatch_riff_read_chunk_verify(riff, IPATCH_RIFF_CHUNK_SUB, IPATCH_SFONT_FOURCC_PBAG, err)) { return (FALSE); } chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size % IPATCH_SFONT_BAG_SIZE || chunk->size / IPATCH_SFONT_BAG_SIZE != reader->pbag_count + 1) { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } bag_table = reader->pbag_table = g_malloc(chunk->size); if(!ipatch_file_read(riff->handle, bag_table, chunk->size, err)) { return (FALSE); /* bag_table will be freed by finalize() */ } pgenndx = IPATCH_FILE_SWAP16(riff->handle->file, &bag_table[0]); pmodndx = IPATCH_FILE_SWAP16(riff->handle->file, &bag_table[1]); for(i = 0; i < reader->pbag_count; i++) { genndx = IPATCH_FILE_SWAP16(riff->handle->file, &bag_table[(i + 1) * 2]); modndx = IPATCH_FILE_SWAP16(riff->handle->file, &bag_table[(i + 1) * 2 + 1]); if(genndx < pgenndx) { g_set_error(err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_INVALID_DATA, "Invalid preset gen index"); return (FALSE); } if(modndx < pmodndx) { g_set_error(err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_INVALID_DATA, "Invalid preset mod index"); return (FALSE); } bag_table[i * 2] = genndx - pgenndx; bag_table[i * 2 + 1] = modndx - pmodndx; pgenndx = genndx; /* update previous zone gen index */ pmodndx = modndx; /* update previous zone mod index */ } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } return (TRUE); } /* preset modulator reader */ static gboolean sfload_pmods(IpatchSF2Reader *reader, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchSF2Zone *zone; IpatchSF2Mod mod; GSList *p, *p2; guint bagmod_index = 1; int i; if(!ipatch_riff_read_chunk_verify(riff, IPATCH_RIFF_CHUNK_SUB, IPATCH_SFONT_FOURCC_PMOD, err)) { return (FALSE); } p = reader->sf->presets; while(p) /* traverse through all presets */ { p2 = IPATCH_SF2_PRESET(p->data)->zones; while(p2) /* traverse this preset's zones */ { zone = IPATCH_SF2_ZONE(p2->data); /* get stored modulator count for zone bag table */ i = reader->pbag_table[bagmod_index]; bagmod_index += 2; while(i-- > 0) /* load modulators */ { if(!ipatch_file_buf_load(riff->handle, IPATCH_SFONT_MOD_SIZE, err)) { return (FALSE); } ipatch_sf2_load_mod(riff->handle, &mod); ipatch_sf2_mod_item_add((IpatchSF2ModItem *)zone, &mod); } p2 = g_slist_next(p2); } p = g_slist_next(p); } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } return (TRUE); } /* preset generator reader */ static gboolean sfload_pgens(IpatchSF2Reader *reader, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchSF2Preset *preset; IpatchSF2Zone *zone; IpatchSF2GenAmount amount; int genid; GSList *p, *p2; guint pbag_index = 0; guint index; int level, discarded; int i; if(!ipatch_riff_read_chunk_verify(riff, IPATCH_RIFF_CHUNK_SUB, IPATCH_SFONT_FOURCC_PGEN, err)) { return (FALSE); } p = reader->sf->presets; while(p) /* traverse through all presets */ { discarded = FALSE; preset = IPATCH_SF2_PRESET(p->data); p2 = preset->zones; while(p2) /* traverse preset's zones */ { level = 0; zone = IPATCH_SF2_ZONE(p2->data); /* retrieve our stored gen count (from load_pbag) */ i = reader->pbag_table[pbag_index]; pbag_index += 2; while(i-- > 0) /* load zone's generators */ { if(!ipatch_file_buf_load(riff->handle, IPATCH_SFONT_GEN_SIZE, err)) { return (FALSE); } ipatch_sf2_load_gen(riff->handle, &genid, &amount); /* check validity of generator */ if((genid != IPATCH_SF2_GEN_INSTRUMENT_ID && !ipatch_sf2_gen_is_valid(genid, TRUE)) || (genid == IPATCH_SF2_GEN_NOTE_RANGE && level != 0) || (genid == IPATCH_SF2_GEN_VELOCITY_RANGE && level > 1)) { discarded = TRUE; continue; } /* IPATCH_SF2_GEN_NOTE_RANGE first (if any) followed by IPATCH_SF2_GEN_VELOCITY_RANGE (if any), IPATCH_SF2_GEN_INSTRUMENT_ID is last */ if(genid == IPATCH_SF2_GEN_NOTE_RANGE) { level = 1; } else if(genid == IPATCH_SF2_GEN_VELOCITY_RANGE) { level = 2; } else if(genid == IPATCH_SF2_GEN_INSTRUMENT_ID) { index = amount.uword; if(index >= reader->inst_count) g_warning(_("Invalid reference in preset '%s'"), preset->name); /* will get discarded below */ else ipatch_sf2_zone_set_link_item(zone, (IpatchItem *) reader->inst_table[index]); level = 3; break; /* break out of gen loop */ } else { level = 2; } /* set the generator */ zone->genarray.values[genid] = amount; IPATCH_SF2_GEN_ARRAY_SET_FLAG(&zone->genarray, genid); } /* generator loop */ /* ignore (skip) any generators following an instrument ID */ while(i-- > 0) { discarded = TRUE; if(!ipatch_file_buf_load(riff->handle, IPATCH_SFONT_GEN_SIZE, err)) { return (FALSE); } } /* if level != 3 (no instrument ID) and not first zone, discard */ if(level != 3 && p2 != preset->zones) { /* discard invalid global zone */ IpatchItem *item = IPATCH_ITEM(p2->data); p2 = g_slist_next(p2); ipatch_container_remove(IPATCH_CONTAINER(preset), item); g_warning(_("Preset \"%s\": Discarding invalid global zone"), preset->name); continue; } p2 = g_slist_next(p2); /* next zone */ } /* global zone? Migrate gens/mods to preset and remove zone. */ if(preset->zones && !((IpatchSF2Zone *)(preset->zones->data))->item) { IpatchSF2Zone *zone = (IpatchSF2Zone *)(preset->zones->data); preset->genarray = zone->genarray; preset->mods = zone->mods; /* snatch modulator list */ zone->mods = NULL; ipatch_container_remove(IPATCH_CONTAINER(preset), (IpatchItem *)zone); } if(discarded) g_warning(_("Preset \"%s\": Some invalid generators were discarded"), preset->name); p = g_slist_next(p); } /* free some no longer needed tables */ g_free(reader->pbag_table); reader->pbag_table = NULL; g_free(reader->inst_table); reader->inst_table = NULL; if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } return (TRUE); } /* instrument header reader */ static gboolean sfload_ihdrs(IpatchSF2Reader *reader, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchSF2Inst *inst = NULL, *prev = NULL; /* current & previous instrument */ IpatchIter inst_iter, zone_iter; IpatchItem *zone; IpatchRiffChunk *chunk; IpatchSF2Inst **iptr; IpatchSF2Ihdr ihdr; guint16 zndx = 0, pzndx = 0; int i, i2; if(!ipatch_riff_read_chunk_verify(riff, IPATCH_RIFF_CHUNK_SUB, IPATCH_SFONT_FOURCC_INST, err)) { return (FALSE); } chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size == 0) { return (TRUE); /* no instrument headers? */ } if(chunk->size % IPATCH_SFONT_INST_SIZE) /* verify chunk size */ { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } /* initialize iterator to instrument list */ if(!ipatch_container_init_iter(IPATCH_CONTAINER(reader->sf), &inst_iter, IPATCH_TYPE_SF2_INST)) { return (FALSE); } i = chunk->size / IPATCH_SFONT_INST_SIZE; /* instrument count + 1 EOI */ reader->inst_count = i - 1; /* allocate instrument table (used for PresetZone fixups) */ iptr = reader->inst_table = g_malloc((i - 1) * sizeof(IpatchSF2Inst *)); /* loop over all instrument headers (including dummy terminal record) */ for(; i > 0; i--) { if(!ipatch_file_buf_load(riff->handle, IPATCH_SFONT_INST_SIZE, err)) { return (FALSE); } ipatch_sf2_load_ihdr(riff->handle, &ihdr); zndx = ihdr.bag_index; if(i != 1) /* don't add terminal record, free instead */ { /* ++ ref new inst and add to instrument fixup table */ *(iptr++) = inst = ipatch_sf2_inst_new(); inst->name = g_strndup(ihdr.name, 20); /* insert the instrument (default is to keep appending) */ ipatch_container_insert_iter((IpatchContainer *)(reader->sf), (IpatchItem *)inst, &inst_iter); g_object_unref(inst); /* -- unref new instrument */ } if(prev) /* not first instrument? */ { if(zndx < pzndx) /* make sure zone index isn't decreasing */ { g_set_error(err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_INVALID_DATA, "Invalid instrument zone index"); return (FALSE); } /* initialize iterator to instrument zone list */ if(!ipatch_container_init_iter((IpatchContainer *)prev, &zone_iter, IPATCH_TYPE_SF2_IZONE)) { return (FALSE); } i2 = zndx - pzndx; /* # of zones in last instrument */ while(i2--) /* create zones for last instrument */ { /* ++ ref new zone and insert it */ zone = g_object_new(IPATCH_TYPE_SF2_IZONE, NULL); ipatch_container_insert_iter((IpatchContainer *)prev, zone, &zone_iter); g_object_unref(zone); /* -- unref new zone */ } } else if(zndx > 0) /* 1st instrument, warn if ofs > 0 */ { g_warning(_("Discarding %d unreferenced instrument zones"), zndx); } prev = inst; /* update previous instrument ptr */ pzndx = zndx; } reader->ibag_count = zndx; /* total number of instrument zones */ if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } return (TRUE); } /* instrument bag reader */ static gboolean sfload_ibags(IpatchSF2Reader *reader, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchRiffChunk *chunk; guint16 *bag_table; guint16 genndx, modndx; guint16 pgenndx, pmodndx; guint i; if(!ipatch_riff_read_chunk_verify(riff, IPATCH_RIFF_CHUNK_SUB, IPATCH_SFONT_FOURCC_IBAG, err)) { return (FALSE); } chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size % IPATCH_SFONT_BAG_SIZE || chunk->size / IPATCH_SFONT_BAG_SIZE != reader->ibag_count + 1) { SET_SIZE_ERROR(riff, -1, err); return (FALSE); } bag_table = reader->ibag_table = g_malloc(chunk->size); if(!ipatch_file_read(riff->handle, bag_table, chunk->size, err)) { return (FALSE); /* bag_table will be freed by finalize() */ } pgenndx = IPATCH_FILE_SWAP16(riff->handle->file, &bag_table[0]); pmodndx = IPATCH_FILE_SWAP16(riff->handle->file, &bag_table[1]); for(i = 0; i < reader->ibag_count; i++) { genndx = IPATCH_FILE_SWAP16(riff->handle->file, &bag_table[(i + 1) * 2]); modndx = IPATCH_FILE_SWAP16(riff->handle->file, &bag_table[(i + 1) * 2 + 1]); if(genndx < pgenndx) { g_set_error(err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_INVALID_DATA, "Invalid instrument gen index"); return (FALSE); } if(modndx < pmodndx) { g_set_error(err, IPATCH_RIFF_ERROR, IPATCH_RIFF_ERROR_INVALID_DATA, "Invalid instrument mod index"); return (FALSE); } bag_table[i * 2] = genndx - pgenndx; bag_table[i * 2 + 1] = modndx - pmodndx; pgenndx = genndx; /* update previous zone gen index */ pmodndx = modndx; /* update previous zone mod index */ } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } return (TRUE); } /* instrument modulator reader */ static gboolean sfload_imods(IpatchSF2Reader *reader, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchSF2Zone *zone; IpatchSF2Mod mod; guint bagmod_index = 1; GSList *p, *p2; int i; if(!ipatch_riff_read_chunk_verify(riff, IPATCH_RIFF_CHUNK_SUB, IPATCH_SFONT_FOURCC_IMOD, err)) { return (FALSE); } p = reader->sf->insts; while(p) /* traverse through all instruments */ { p2 = IPATCH_SF2_INST(p->data)->zones; while(p2) /* traverse this instrument's zones */ { zone = IPATCH_SF2_ZONE(p2->data); /* stored modulator count */ i = reader->ibag_table[bagmod_index]; bagmod_index += 2; while(i-- > 0) /* load zone's modulators */ { if(!ipatch_file_buf_load(riff->handle, IPATCH_SFONT_MOD_SIZE, err)) { return (FALSE); } ipatch_sf2_load_mod(riff->handle, &mod); ipatch_sf2_mod_item_add((IpatchSF2ModItem *)zone, &mod); } p2 = g_slist_next(p2); } p = g_slist_next(p); } if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } return (TRUE); } /* instrument generator reader */ static gboolean sfload_igens(IpatchSF2Reader *reader, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchSF2Inst *inst; IpatchSF2Zone *zone; IpatchSF2GenAmount amount; int genid; GSList *p, *p2; guint ibag_index = 0; guint index; int level, discarded; int i; if(!ipatch_riff_read_chunk_verify(riff, IPATCH_RIFF_CHUNK_SUB, IPATCH_SFONT_FOURCC_IGEN, err)) { return (FALSE); } p = reader->sf->insts; while(p) /* traverse through all instruments */ { discarded = FALSE; inst = IPATCH_SF2_INST(p->data); p2 = inst->zones; while(p2) /* traverse instruments's zones */ { level = 0; zone = IPATCH_SF2_ZONE(p2->data); /* retrieve our stored gen count (from load_ibag) */ i = reader->ibag_table[ibag_index]; ibag_index += 2; while(i--) /* load zone's generators */ { if(!ipatch_file_buf_load(riff->handle, IPATCH_SFONT_GEN_SIZE, err)) { return (FALSE); } ipatch_sf2_load_gen(riff->handle, &genid, &amount); /* check validity of generator */ if((genid != IPATCH_SF2_GEN_SAMPLE_ID && !ipatch_sf2_gen_is_valid(genid, FALSE)) || (genid == IPATCH_SF2_GEN_NOTE_RANGE && level != 0) || (genid == IPATCH_SF2_GEN_VELOCITY_RANGE && level > 1)) { discarded = TRUE; continue; } /* IPATCH_SF2_GEN_NOTE_RANGE first (if any) followed by IPATCH_SF2_GEN_VELOCITY_RANGE (if any), IPATCH_SF2_GEN_SAMPLE_ID is last */ if(genid == IPATCH_SF2_GEN_NOTE_RANGE) { level = 1; } else if(genid == IPATCH_SF2_GEN_VELOCITY_RANGE) { level = 2; } else if(genid == IPATCH_SF2_GEN_SAMPLE_ID) { index = amount.uword; if(index >= reader->sample_count) g_warning(_("Invalid reference in instrument '%s'"), inst->name); /* will get discarded below */ else ipatch_sf2_zone_set_link_item(zone, (IpatchItem *) reader->sample_table[index]); level = 3; break; /* break out of gen loop */ } else { level = 2; } /* set the generator */ zone->genarray.values[genid] = amount; IPATCH_SF2_GEN_ARRAY_SET_FLAG(&zone->genarray, genid); } /* generator loop */ /* ignore (skip) any generators following a sample ID */ while(i-- > 0) { discarded = TRUE; if(!ipatch_file_buf_load(riff->handle, IPATCH_SFONT_GEN_SIZE, err)) { return (FALSE); } } /* if level !=3 (no sample ID) and not first zone, discard */ if(level != 3 && p2 != inst->zones) { /* discard invalid global zone */ IpatchItem *item = IPATCH_ITEM(p2->data); p2 = g_slist_next(p2); ipatch_container_remove(IPATCH_CONTAINER(inst), item); g_warning(_("Instrument \"%s\": Discarding invalid" " global zone"), inst->name); continue; } p2 = g_slist_next(p2); /* next zone */ } /* global zone? Migrate gens/mods to instrument and remove zone. */ if(inst->zones && !((IpatchSF2Zone *)(inst->zones->data))->item) { IpatchSF2Zone *zone = (IpatchSF2Zone *)(inst->zones->data); inst->genarray = zone->genarray; inst->mods = zone->mods; /* snatch modulator list */ zone->mods = NULL; ipatch_container_remove(IPATCH_CONTAINER(inst), (IpatchItem *)zone); } if(discarded) g_warning(_("Instrument \"%s\": Some invalid generators" " were discarded"), inst->name); p = g_slist_next(p); /* next instrument */ } /* free some no longer needed tables */ g_free(reader->ibag_table); reader->ibag_table = NULL; g_free(reader->sample_table); reader->sample_table = NULL; if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } return (TRUE); } /* sample header reader */ static gboolean sfload_shdrs(IpatchSF2Reader *reader, GError **err) { IpatchRiff *riff = IPATCH_RIFF(reader); IpatchRiffChunk *chunk; IpatchSF2Sample *sample, *link_sample, **sptr; IpatchSampleData *sampledata; IpatchSample *store; IpatchIter iter; IpatchSF2Shdr shdr; guint samchunk_pos, samchunk_size, sam24chunk_pos; guint openlink_count = 0; char *filename; guint32 i, count; if(!ipatch_riff_read_chunk_verify(riff, IPATCH_RIFF_CHUNK_SUB, IPATCH_SFONT_FOURCC_SHDR, err)) { return (FALSE); } g_object_get(IPATCH_BASE(reader->sf)->file, "file-name", &filename, /* ++ alloc filename */ "sample-pos", &samchunk_pos, "sample-size", &samchunk_size, "sample24-pos", &sam24chunk_pos, NULL); chunk = ipatch_riff_get_chunk(riff, -1); if(chunk->size <= IPATCH_SFONT_SHDR_SIZE) { return (TRUE); /* no samples? */ } if(chunk->size % IPATCH_SFONT_SHDR_SIZE) /* verify chunk size */ { SET_SIZE_ERROR(riff, -1, err); g_free(filename); /* -- free filename */ return (FALSE); } /* initialize iterator to sample list */ if(!ipatch_container_init_iter(IPATCH_CONTAINER(reader->sf), &iter, IPATCH_TYPE_SF2_SAMPLE)) { return (FALSE); } /* get number of sample headers */ count = chunk->size / IPATCH_SFONT_SHDR_SIZE - 1; reader->sample_count = count; /* allocate sample fixup table (to fixup instrument zone references) */ sptr = reader->sample_table = g_malloc(count * sizeof(IpatchSF2Sample *)); /* load all sample headers (not including terminal record) */ for(i = 0; i < count; i++) { if(!ipatch_file_buf_load(riff->handle, IPATCH_SFONT_SHDR_SIZE, err)) { g_free(filename); /* -- free filename */ return (FALSE); } ipatch_sf2_load_shdr(riff->handle, &shdr); /* ++ ref new sample and add to sample fixup table */ *(sptr++) = sample = g_object_new(IPATCH_TYPE_SF2_SAMPLE, NULL); sample->name = g_strndup(shdr.name, 20); /* sample NOT totally screwed? (ROM or within sample chunk, ROM or sample chunk actually exists, at least 4 samples in length) */ if(((shdr.type & IPATCH_SF2_FILE_SAMPLE_TYPE_ROM) || shdr.end <= samchunk_size) && ((shdr.type & IPATCH_SF2_FILE_SAMPLE_TYPE_ROM) || samchunk_pos > 0) && (shdr.start < shdr.end && shdr.end - shdr.start > 4)) { if(shdr.loop_start < shdr.start) { g_warning(_("Sample '%s' loop start begins before sample data, setting to offset 0"), sample->name); sample->loop_start = 0; } else { sample->loop_start = shdr.loop_start - shdr.start; } if(shdr.loop_end < shdr.start) { g_warning(_("Sample '%s' loop end begins before sample data, setting to offset 0"), sample->name); sample->loop_end = 0; } else { sample->loop_end = shdr.loop_end - shdr.start; } /* Keep invalid loop indexes since instrument zones offsets may correct them. * In particular samples have been seen with end loop points 1 sample off the * end of the sample. Output warning though. */ if(shdr.loop_end > shdr.end || shdr.loop_start >= shdr.loop_end) g_warning(_("Sample '%s' has invalid loop, keeping it (start:%u end:%u loop_start:%u loop_end:%u)"), sample->name, shdr.start, shdr.end, shdr.loop_start, shdr.loop_end); sample->rate = shdr.rate; sample->root_note = shdr.root_note; sample->fine_tune = shdr.fine_tune; if(shdr.type & IPATCH_SF2_FILE_SAMPLE_TYPE_RIGHT) { sample->channel = IPATCH_SF2_SAMPLE_CHANNEL_RIGHT; } else if(shdr.type & IPATCH_SF2_FILE_SAMPLE_TYPE_LEFT) { sample->channel = IPATCH_SF2_SAMPLE_CHANNEL_LEFT; } else { sample->channel = IPATCH_SF2_SAMPLE_CHANNEL_MONO; } /* check for stereo linked sample */ if((shdr.type & IPATCH_SF2_FILE_SAMPLE_TYPE_RIGHT || shdr.type & IPATCH_SF2_FILE_SAMPLE_TYPE_LEFT)) { if(shdr.link_index < i) /* can we fixup link? */ { link_sample = reader->sample_table[shdr.link_index]; if(!ipatch_sf2_sample_peek_linked(link_sample)) /* sample not already linked? */ { /* FIXME - Ensure same size samples */ ipatch_sf2_sample_set_linked(sample, link_sample); ipatch_sf2_sample_set_linked(link_sample, sample); if(ipatch_item_get_flags(link_sample) & (1 << 31)) { openlink_count--; ipatch_item_clear_flags(link_sample, 1 << 31); } } else /* sample is already linked */ g_warning(_("Duplicate stereo link to sample '%s'" " from '%s'"), link_sample->name, sample->name); } else /* could not fixup stereo link, do later (below) */ { openlink_count++; ipatch_item_set_flags(sample, 1 << 31); } } /* sample is not a ROM sample? */ if(!(shdr.type & IPATCH_SF2_FILE_SAMPLE_TYPE_ROM)) { /* SoundFont contains 24 bit audio? */ if(sam24chunk_pos > 0) { /* ++ ref new split 24 bit sample store */ store = ipatch_sample_store_split24_new(riff->handle->file, samchunk_pos + shdr.start * 2, sam24chunk_pos + shdr.start); /* use host endian, Split24 stores will transform as necessary */ ipatch_sample_set_format(store, IPATCH_SAMPLE_24BIT | IPATCH_SAMPLE_MONO | IPATCH_SAMPLE_SIGNED | IPATCH_SAMPLE_ENDIAN_HOST); } else /* 16 bit samples */ { /* ++ ref new file sample store */ store = ipatch_sample_store_file_new(riff->handle->file, samchunk_pos + shdr.start * 2); ipatch_sample_set_format(store, IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_MONO | IPATCH_SAMPLE_SIGNED | IPATCH_SAMPLE_LENDIAN); } } else /* ROM sample, create ROM sample store */ { /* Set ROM flag */ ipatch_item_set_flags(sample, IPATCH_SF2_SAMPLE_FLAG_ROM); /* ++ ref new ROM sample store */ store = ipatch_sample_store_rom_new(shdr.start * 2); ipatch_sample_set_format(store, IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_MONO | IPATCH_SAMPLE_SIGNED | IPATCH_SAMPLE_LENDIAN); } g_object_set(store, "sample-size", shdr.end - shdr.start, "sample-rate", shdr.rate, NULL); sampledata = ipatch_sample_data_new(); /* ++ ref */ ipatch_sample_data_add(sampledata, (IpatchSampleStore *)store); ipatch_sf2_sample_set_data(sample, sampledata); g_object_unref(store); /* -- unref sample store */ g_object_unref(sampledata); /* -- unref sampledata */ } else /* sample is messed, set to blank data */ { g_warning(_("Invalid sample '%s'"), sample->name); ipatch_sf2_sample_set_blank(sample); } /* insert sample into SoundFont */ ipatch_container_insert_iter((IpatchContainer *)(reader->sf), (IpatchItem *)sample, &iter); g_object_unref(sample); /* -- unref new sample */ } if(openlink_count > 0) /* any unresolved linked stereo samples? */ { count = reader->sample_count; for(i = 0; i < reader->sample_count; i++) { sample = reader->sample_table[i]; if((ipatch_item_get_flags(sample) & (1 << 31)) && !ipatch_sf2_sample_peek_linked(sample)) { g_warning(_("Invalid stereo link for sample '%s'"), sample->name); sample->channel = IPATCH_SF2_SAMPLE_CHANNEL_MONO; } } } g_free(filename); /* -- free filename */ if(!ipatch_riff_end_chunk(riff, err)) { return (FALSE); } return (TRUE); } libinstpatch-1.1.6/libinstpatch/IpatchSF2Reader.h000066400000000000000000000050371400263525300217130ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_READER_H__ #define __IPATCH_SF2_READER_H__ #include #include #include #include #include #include typedef struct _IpatchSF2Reader IpatchSF2Reader; /* private structure */ typedef struct _IpatchSF2ReaderClass IpatchSF2ReaderClass; #define IPATCH_TYPE_SF2_READER (ipatch_sf2_reader_get_type ()) #define IPATCH_SF2_READER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SF2_READER, \ IpatchSF2Reader)) #define IPATCH_SF2_READER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SF2_READER, \ IpatchSF2ReaderClass)) #define IPATCH_IS_SF2_READER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SF2_READER)) #define IPATCH_IS_SF2_READER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SF2_READER)) /* SoundFont parser object */ struct _IpatchSF2Reader { IpatchRiff parent_instance; /* derived from IpatchRiff */ IpatchSF2 *sf; /* SoundFont object to load file into */ /*< private >*/ guint16 *pbag_table; guint pbag_count; guint16 *ibag_table; guint ibag_count; IpatchSF2Inst **inst_table; guint inst_count; IpatchSF2Sample **sample_table; guint sample_count; }; /* RIFF parser class */ struct _IpatchSF2ReaderClass { IpatchRiffClass parent_class; }; GType ipatch_sf2_reader_get_type(void); IpatchSF2Reader *ipatch_sf2_reader_new(IpatchFileHandle *handle); void ipatch_sf2_reader_set_file_handle(IpatchSF2Reader *reader, IpatchFileHandle *handle); IpatchSF2 *ipatch_sf2_reader_load(IpatchSF2Reader *reader, GError **err); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2Sample.c000066400000000000000000000655501400263525300217330ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2Sample * @short_description: SoundFont audio sample * @see_also: #IpatchSF2, #IpatchSF2IZone * @stability: Stable * * SoundFont samples are children of #IpatchSF2 objects and are referenced * by #IpatchSF2IZone objects. They define the audio which is synthesized. */ #include #include #include #include "IpatchSF2Sample.h" #include "IpatchSample.h" #include "IpatchSampleData.h" #include "IpatchSF2.h" #include "IpatchSF2File.h" #include "IpatchParamProp.h" #include "IpatchTypeProp.h" #include "IpatchSF2VoiceCache.h" #include "builtin_enums.h" #include "ipatch_priv.h" /* properties */ enum { PROP_0, PROP_NAME, PROP_SAMPLE_SIZE, PROP_SAMPLE_FORMAT, PROP_SAMPLE_RATE, PROP_LOOP_TYPE, PROP_LOOP_START, PROP_LOOP_END, PROP_ROOT_NOTE, PROP_FINE_TUNE, PROP_CHANNEL, PROP_ROM, PROP_SAMPLE_DATA, PROP_LINKED_SAMPLE }; static void ipatch_sf2_sample_iface_init(IpatchSampleIface *sample_iface); static gboolean ipatch_sf2_sample_iface_open(IpatchSampleHandle *handle, GError **err); static void ipatch_sf2_sample_finalize(GObject *gobject); static void ipatch_sf2_sample_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sf2_sample_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sf2_sample_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static void ipatch_sf2_sample_item_remove_full(IpatchItem *item, gboolean full); static int ipatch_sf2_sample_voice_cache_update_handler(IpatchSF2VoiceCache *cache, int *select_values, GObject *cache_item, GObject *item, GParamSpec *pspec, const GValue *value, IpatchSF2VoiceUpdate *updates, guint max_updates); static void ipatch_sf2_sample_real_set_name(IpatchSF2Sample *sample, const char *name, gboolean name_notify); static void ipatch_sf2_sample_real_set_data(IpatchSF2Sample *sample, IpatchSampleData *sampledata, gboolean data_notify); static void ipatch_sf2_sample_real_set_linked(IpatchSF2Sample *sample, IpatchSF2Sample *linked, gboolean linked_notify); /* cached parameter spec values for speed */ static GParamSpec *name_pspec; static GParamSpec *sample_data_pspec; static GParamSpec *linked_sample_pspec; G_DEFINE_TYPE_WITH_CODE(IpatchSF2Sample, ipatch_sf2_sample, IPATCH_TYPE_ITEM, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_sf2_sample_iface_init)) /* sample interface initialization */ static void ipatch_sf2_sample_iface_init(IpatchSampleIface *sample_iface) { sample_iface->open = ipatch_sf2_sample_iface_open; } static gboolean ipatch_sf2_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchSF2Sample *sample = IPATCH_SF2_SAMPLE(handle->sample); g_return_val_if_fail(sample->sample_data != NULL, FALSE); return (ipatch_sample_handle_cascade_open (handle, (IpatchSample *)(sample->sample_data), err)); } static void ipatch_sf2_sample_class_init(IpatchSF2SampleClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); GParamSpec *pspec; obj_class->finalize = ipatch_sf2_sample_finalize; obj_class->get_property = ipatch_sf2_sample_get_property; /* we use the IpatchItem item_set_property method */ item_class->item_set_property = ipatch_sf2_sample_set_property; item_class->copy = ipatch_sf2_sample_item_copy; item_class->remove_full = ipatch_sf2_sample_item_remove_full; /* "name" property is used as the title */ g_object_class_override_property(obj_class, PROP_NAME, "title"); name_pspec = g_param_spec_string("name", "Name", "Name", NULL, G_PARAM_READWRITE | IPATCH_PARAM_UNIQUE); ipatch_param_set(name_pspec, "string-max-length", IPATCH_SFONT_NAME_SIZE, /* max length */ NULL); g_object_class_install_property(obj_class, PROP_NAME, name_pspec); /* properties defined by IpatchSample interface */ ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_SIZE, "sample-size"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_FORMAT, "sample-format"); pspec = ipatch_sample_install_property(obj_class, PROP_SAMPLE_RATE, "sample-rate"); pspec->flags |= IPATCH_PARAM_SYNTH; /* IpatchSF2Sample object's don't have a loop type field really */ ipatch_sample_install_property_readonly(obj_class, PROP_LOOP_TYPE, "loop-type"); pspec = ipatch_sample_install_property(obj_class, PROP_LOOP_START, "loop-start"); pspec->flags |= IPATCH_PARAM_SYNTH | IPATCH_PARAM_SYNTH_REALTIME; pspec = ipatch_sample_install_property(obj_class, PROP_LOOP_END, "loop-end"); pspec->flags |= IPATCH_PARAM_SYNTH | IPATCH_PARAM_SYNTH_REALTIME; pspec = ipatch_sample_install_property(obj_class, PROP_ROOT_NOTE, "root-note"); pspec->flags |= IPATCH_PARAM_SYNTH; pspec = ipatch_sample_install_property(obj_class, PROP_FINE_TUNE, "fine-tune"); pspec->flags |= IPATCH_PARAM_SYNTH | IPATCH_PARAM_SYNTH_REALTIME; g_object_class_install_property(obj_class, PROP_CHANNEL, g_param_spec_enum("channel", _("Channel orientation"), _("Channel orientation"), IPATCH_TYPE_SF2_SAMPLE_CHANNEL, IPATCH_SF2_SAMPLE_CHANNEL_MONO, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_ROM, g_param_spec_boolean("rom", _("ROM sample flag"), _("ROM sample flag"), FALSE, G_PARAM_READWRITE)); sample_data_pspec = ipatch_sample_install_property(obj_class, PROP_SAMPLE_DATA, "sample-data"); linked_sample_pspec = g_param_spec_object("linked-sample", _("Linked sample"), _("Stereo linked sample object"), IPATCH_TYPE_SF2_SAMPLE, G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_LINKED_SAMPLE, linked_sample_pspec); /* install IpatchSF2VoiceCache update handler for real time effects */ ipatch_type_set(IPATCH_TYPE_SF2_SAMPLE, "sf2voice-update-func", ipatch_sf2_sample_voice_cache_update_handler, NULL); } static void ipatch_sf2_sample_init(IpatchSF2Sample *sample) { ipatch_sf2_sample_set_blank(sample); sample->rate = IPATCH_SAMPLE_RATE_DEFAULT; g_weak_ref_init(&sample->linked, NULL); } static void ipatch_sf2_sample_finalize(GObject *gobject) { IpatchSF2Sample *sample = IPATCH_SF2_SAMPLE(gobject); /* nothing should reference the sample after this, but we set pointers to NULL to help catch invalid references. Locking of sample is required since in reality all its children do still hold references */ ipatch_sf2_sample_real_set_data(sample, NULL, FALSE); IPATCH_ITEM_WLOCK(sample); g_weak_ref_clear(&sample->linked); g_free(sample->name); sample->name = NULL; IPATCH_ITEM_WUNLOCK(sample); if(G_OBJECT_CLASS(ipatch_sf2_sample_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_sf2_sample_parent_class)->finalize(gobject); } } static void ipatch_sf2_sample_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSF2Sample *sample = IPATCH_SF2_SAMPLE(object); switch(property_id) { case PROP_NAME: ipatch_sf2_sample_real_set_name(sample, g_value_get_string(value), FALSE); /* don't do name notify */ break; case PROP_SAMPLE_RATE: IPATCH_ITEM_WLOCK(sample); sample->rate = g_value_get_int(value); IPATCH_ITEM_WUNLOCK(sample); break; case PROP_LOOP_START: IPATCH_ITEM_WLOCK(sample); sample->loop_start = g_value_get_uint(value); IPATCH_ITEM_WUNLOCK(sample); break; case PROP_LOOP_END: IPATCH_ITEM_WLOCK(sample); sample->loop_end = g_value_get_uint(value); IPATCH_ITEM_WUNLOCK(sample); break; case PROP_ROOT_NOTE: IPATCH_ITEM_WLOCK(sample); sample->root_note = g_value_get_int(value); IPATCH_ITEM_WUNLOCK(sample); break; case PROP_FINE_TUNE: IPATCH_ITEM_WLOCK(sample); sample->fine_tune = g_value_get_int(value); IPATCH_ITEM_WUNLOCK(sample); break; case PROP_CHANNEL: sample->channel = g_value_get_enum(value); break; case PROP_ROM: ipatch_item_set_flags((IpatchItem *)object, g_value_get_boolean(value) << IPATCH_SF2_SAMPLE_FLAG_ROM); break; case PROP_SAMPLE_DATA: ipatch_sf2_sample_real_set_data(sample, (IpatchSampleData *) g_value_get_object(value), FALSE); break; case PROP_LINKED_SAMPLE: ipatch_sf2_sample_real_set_linked(sample, (IpatchSF2Sample *) g_value_get_object(value), FALSE); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_sf2_sample_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSF2Sample *sample; g_return_if_fail(IPATCH_IS_SF2_SAMPLE(object)); sample = IPATCH_SF2_SAMPLE(object); switch(property_id) { case PROP_NAME: g_value_take_string(value, ipatch_sf2_sample_get_name(sample)); break; case PROP_SAMPLE_SIZE: g_return_if_fail(sample->sample_data != NULL); g_value_set_uint(value, ipatch_sample_get_size((IpatchSample *)(sample->sample_data), NULL)); break; case PROP_SAMPLE_FORMAT: g_return_if_fail(sample->sample_data != NULL); g_value_set_int(value, ipatch_sample_get_format((IpatchSample *)(sample->sample_data))); break; case PROP_SAMPLE_RATE: IPATCH_ITEM_RLOCK(sample); g_value_set_int(value, sample->rate); IPATCH_ITEM_RUNLOCK(sample); break; case PROP_LOOP_TYPE: /* IpatchSF2Sample objects don't have loop type, just use normal loop */ g_value_set_enum(value, IPATCH_SAMPLE_LOOP_STANDARD); break; case PROP_LOOP_START: IPATCH_ITEM_RLOCK(sample); g_value_set_uint(value, sample->loop_start); IPATCH_ITEM_RUNLOCK(sample); break; case PROP_LOOP_END: IPATCH_ITEM_RLOCK(sample); g_value_set_uint(value, sample->loop_end); IPATCH_ITEM_RUNLOCK(sample); break; case PROP_ROOT_NOTE: g_value_set_int(value, sample->root_note); break; case PROP_FINE_TUNE: g_value_set_int(value, sample->fine_tune); break; case PROP_CHANNEL: g_value_set_enum(value, sample->channel); break; case PROP_ROM: g_value_set_boolean(value, (ipatch_item_get_flags((IpatchItem *)object) & IPATCH_SF2_SAMPLE_FLAG_ROM) != 0); break; case PROP_SAMPLE_DATA: g_value_take_object(value, ipatch_sf2_sample_get_data(sample)); break; case PROP_LINKED_SAMPLE: g_value_take_object(value, ipatch_sf2_sample_get_linked(sample)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_sf2_sample_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchSF2Sample *src_sam, *dest_sam; IpatchItem *linked, *src_linked; src_sam = IPATCH_SF2_SAMPLE(src); dest_sam = IPATCH_SF2_SAMPLE(dest); IPATCH_ITEM_RLOCK(src_sam); ipatch_sf2_sample_set_data(dest_sam, src_sam->sample_data); dest_sam->name = g_strdup(src_sam->name); dest_sam->rate = src_sam->rate; dest_sam->loop_start = src_sam->loop_start; dest_sam->loop_end = src_sam->loop_end; dest_sam->root_note = src_sam->root_note; dest_sam->fine_tune = src_sam->fine_tune; dest_sam->channel = src_sam->channel; if(ipatch_item_get_flags(src_sam) & IPATCH_SF2_SAMPLE_FLAG_ROM) { ipatch_item_set_flags(dest_sam, IPATCH_SF2_SAMPLE_FLAG_ROM); } src_linked = (IpatchItem *)ipatch_sf2_sample_get_linked(src_sam); // ++ ref src linked sample if(src_linked) { linked = IPATCH_ITEM_COPY_LINK_FUNC(dest, src_linked, link_func, user_data); g_object_unref(src_linked); // -- unref src linked sample if(linked) { ipatch_sf2_sample_set_linked(dest_sam, IPATCH_SF2_SAMPLE(linked)); } } IPATCH_ITEM_RUNLOCK(src_sam); } static void ipatch_sf2_sample_item_remove_full(IpatchItem *item, gboolean full) { IpatchList *list; IpatchSF2Sample *linked; IpatchItem *zitem, *temp; IpatchIter iter; list = ipatch_sf2_get_zone_references(item); /* ++ ref zone list */ ipatch_list_init_iter(list, &iter); zitem = ipatch_item_first(&iter); while(zitem) { temp = zitem; zitem = ipatch_item_next(&iter); ipatch_item_remove(temp); } g_object_unref(list); /* -- unref list */ linked = ipatch_sf2_sample_get_linked(IPATCH_SF2_SAMPLE(item)); /* ++ ref */ if(linked) { ipatch_sf2_sample_set_linked(linked, NULL); /* clear link to item */ g_object_unref(linked); /* -- unref linked sample */ if(full) { ipatch_sf2_sample_set_linked(IPATCH_SF2_SAMPLE(item), NULL); } } if(full) { ipatch_sf2_sample_set_data(IPATCH_SF2_SAMPLE(item), NULL); } if(IPATCH_ITEM_CLASS(ipatch_sf2_sample_parent_class)->remove_full) { IPATCH_ITEM_CLASS(ipatch_sf2_sample_parent_class)->remove_full(item, full); } } /* IpatchSF2VoiceCache update function for realtime effects */ static int ipatch_sf2_sample_voice_cache_update_handler(IpatchSF2VoiceCache *cache, int *select_values, GObject *cache_item, GObject *item, GParamSpec *pspec, const GValue *value, IpatchSF2VoiceUpdate *updates, guint max_updates) { IpatchSF2Voice *voice; guint8 genid, genid2 = 255; gint16 val = 0, val2 = 0; int ival; g_return_val_if_fail(cache->voices->len > 0, 0); voice = IPATCH_SF2_VOICE_CACHE_GET_VOICE(cache, 0); switch(IPATCH_PARAM_SPEC_ID(pspec)) { case PROP_LOOP_START: genid = IPATCH_SF2_GEN_SAMPLE_LOOP_START; genid2 = IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START; ival = (int)g_value_get_uint(value) - voice->loop_start; val = ival % 32768; val2 = ival / 32768; break; case PROP_LOOP_END: genid = IPATCH_SF2_GEN_SAMPLE_LOOP_END; genid2 = IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END; ival = (int)g_value_get_uint(value) - voice->loop_end; val = ival % 32768; val2 = ival / 32768; break; case PROP_FINE_TUNE: genid = IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE; ival = g_value_get_int(value); break; default: return (0); } updates->voice = 0; updates->genid = genid; updates->ival = val; if(genid2 != 255 && max_updates >= 2) { updates[1].voice = 0; updates[1].genid = genid2; updates[1].ival = val2; return (2); } else { return (1); } } /** * ipatch_sf2_sample_new: * * Create a new SoundFont sample object. * * Returns: New SoundFont sample with a reference count of 1. Caller * owns the reference and removing it will destroy the item, unless another * reference is added (if its parented for example). */ IpatchSF2Sample * ipatch_sf2_sample_new(void) { return (IPATCH_SF2_SAMPLE(g_object_new(IPATCH_TYPE_SF2_SAMPLE, NULL))); } /** * ipatch_sf2_sample_first: (skip) * @iter: Patch item iterator containing #IpatchSF2Sample items * * Gets the first item in a sample iterator. A convenience wrapper for * ipatch_iter_first(). * * Returns: The first sample in @iter or %NULL if empty. */ IpatchSF2Sample * ipatch_sf2_sample_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_SF2_SAMPLE(obj)); } else { return (NULL); } } /** * ipatch_sf2_sample_next: (skip) * @iter: Patch item iterator containing #IpatchSF2Sample items * * Gets the next item in a sample iterator. A convenience wrapper for * ipatch_iter_next(). * * Returns: The next sample in @iter or %NULL if at the end of the list. */ IpatchSF2Sample * ipatch_sf2_sample_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_SF2_SAMPLE(obj)); } else { return (NULL); } } /** * ipatch_sf2_sample_set_name: * @sample: Sample to set name of * @name: (nullable): Value to set name to * * Sets the name of a SoundFont sample. */ void ipatch_sf2_sample_set_name(IpatchSF2Sample *sample, const char *name) { g_return_if_fail(IPATCH_IS_SF2_SAMPLE(sample)); ipatch_sf2_sample_real_set_name(sample, name, TRUE); } /* also called from item_set_property method so "name_notify" can be used to stop double emission of name notify */ static void ipatch_sf2_sample_real_set_name(IpatchSF2Sample *sample, const char *name, gboolean name_notify) { GValue oldval = { 0 }, newval = { 0 }; char *newname; g_value_init(&oldval, G_TYPE_STRING); newname = g_strdup(name); IPATCH_ITEM_WLOCK(sample); g_value_take_string(&oldval, sample->name); sample->name = newname; IPATCH_ITEM_WUNLOCK(sample); g_value_init(&newval, G_TYPE_STRING); g_value_set_static_string(&newval, name); ipatch_item_prop_notify((IpatchItem *)sample, ipatch_item_pspec_title, &newval, &oldval); if(name_notify) { ipatch_item_prop_notify((IpatchItem *)sample, name_pspec, &newval, &oldval); } g_value_unset(&oldval); g_value_unset(&newval); } /** * ipatch_sf2_sample_get_name: * @sample: Sample to get name of * * Gets the name of a SoundFont sample. * * Returns: (nullable) (transfer full): Name of sample or %NULL if not set. * String value should be freed when finished with it. */ char * ipatch_sf2_sample_get_name(IpatchSF2Sample *sample) { char *name = NULL; g_return_val_if_fail(IPATCH_IS_SF2_SAMPLE(sample), NULL); IPATCH_ITEM_RLOCK(sample); if(sample->name) { name = g_strdup(sample->name); } IPATCH_ITEM_RUNLOCK(sample); return (name); } /** * ipatch_sf2_sample_set_data: * @sample: Sample to set sample data of * @sampledata: (nullable): Sample data to set sample to * * Set a sample's sample data object. */ void ipatch_sf2_sample_set_data(IpatchSF2Sample *sample, IpatchSampleData *sampledata) { g_return_if_fail(IPATCH_IS_SF2_SAMPLE(sample)); g_return_if_fail(!sampledata || IPATCH_IS_SAMPLE_DATA(sampledata)); ipatch_sf2_sample_real_set_data(sample, sampledata, TRUE); } /* the actual setting of sample data, user routine does a g_object_notify */ static void ipatch_sf2_sample_real_set_data(IpatchSF2Sample *sample, IpatchSampleData *sampledata, gboolean data_notify) { GValue oldval = { 0 }, newval = { 0 }; IpatchSampleData *old_sampledata; if(sampledata) { g_object_ref(sampledata); ipatch_sample_data_used(sampledata); /* ++ inc use count */ } IPATCH_ITEM_WLOCK(sample); old_sampledata = sample->sample_data; sample->sample_data = sampledata; IPATCH_ITEM_WUNLOCK(sample); if(old_sampledata) { ipatch_sample_data_unused(old_sampledata); // -- dec use count } if(data_notify) { g_value_init(&newval, IPATCH_TYPE_SAMPLE_DATA); g_value_set_object(&newval, sampledata); g_value_init(&oldval, IPATCH_TYPE_SAMPLE_DATA); g_value_take_object(&oldval, old_sampledata); ipatch_item_prop_notify((IpatchItem *)sample, sample_data_pspec, &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } else if(old_sampledata) { g_object_unref(old_sampledata); // -- unref } } /** * ipatch_sf2_sample_get_data: * @sample: Sample to get sample data from * * Get the #IpatchSampleData item of a sample. Sample data item is referenced * before returning and caller is responsible for unreferencing it with * g_object_unref() when finished with it. * * Returns: (transfer full): Sample data object of sample or %NULL if none. Remember to * unreference with g_object_unref() when finished with it. */ IpatchSampleData * ipatch_sf2_sample_get_data(IpatchSF2Sample *sample) { IpatchSampleData *sampledata; g_return_val_if_fail(IPATCH_IS_SF2_SAMPLE(sample), NULL); IPATCH_ITEM_RLOCK(sample); sampledata = sample->sample_data; if(sampledata) { g_object_ref(sampledata); } IPATCH_ITEM_RUNLOCK(sample); return (sampledata); } /** * ipatch_sf2_sample_peek_data: (skip) * @sample: Sample to get sample data from * * Get the #IpatchSampleData item of a sample. Like * ipatch_sf2_sample_get_data() but sample data object is not referenced. * This function should only be used if a reference of the sample data object * is ensured or only the pointer value is of importance. * * Returns: (transfer none): Sample data object of sample or %NULL if none. * Remember that a reference is NOT added. */ IpatchSampleData * ipatch_sf2_sample_peek_data(IpatchSF2Sample *sample) { IpatchSampleData *sampledata; g_return_val_if_fail(IPATCH_IS_SF2_SAMPLE(sample), NULL); IPATCH_ITEM_RLOCK(sample); sampledata = sample->sample_data; IPATCH_ITEM_RUNLOCK(sample); return (sampledata); } /** * ipatch_sf2_sample_set_linked: * @sample: Sample to set linked sample of * @linked: (nullable): Sample that is stereo linked to @sample or %NULL to unset. * * Sets the stereo linked sample of a sample item. */ void ipatch_sf2_sample_set_linked(IpatchSF2Sample *sample, IpatchSF2Sample *linked) { g_return_if_fail(IPATCH_IS_SF2_SAMPLE(sample)); g_return_if_fail(!linked || IPATCH_IS_SF2_SAMPLE(linked)); ipatch_sf2_sample_real_set_linked(sample, linked, TRUE); } /* real set linked sample */ static void ipatch_sf2_sample_real_set_linked(IpatchSF2Sample *sample, IpatchSF2Sample *linked, gboolean linked_notify) { GValue oldval = { 0 }, newval = { 0 }; GObject *old_linked; IPATCH_ITEM_WLOCK(sample); if(linked_notify) { old_linked = g_weak_ref_get(&sample->linked); // ++ ref old linked item } g_weak_ref_set(&sample->linked, linked); IPATCH_ITEM_WUNLOCK(sample); if(linked_notify) { g_value_init(&oldval, IPATCH_TYPE_SF2_SAMPLE); g_value_take_object(&oldval, old_linked); // !! value takes over old linked item g_value_init(&newval, IPATCH_TYPE_SF2_SAMPLE); g_value_set_object(&newval, linked); ipatch_item_prop_notify((IpatchItem *)sample, sample_data_pspec, &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } } /** * ipatch_sf2_sample_get_linked: * @sample: Sample to get linked sample from * * Get the stereo linked sample from @sample. If a sample is returned the * caller owns a reference and should unref it with g_object_unref() * when finished with it. * * Returns: (transfer full): The linked stereo sample or %NULL if no linked sample. Remember to * unref the returned sample with g_object_unref() when finished with it. */ IpatchSF2Sample * ipatch_sf2_sample_get_linked(IpatchSF2Sample *sample) { IpatchSF2Sample *linked; IPATCH_ITEM_RLOCK(sample); linked = g_weak_ref_get(&sample->linked); // ++ ref linked IPATCH_ITEM_RUNLOCK(sample); return (linked); // !! caller takes over reference } /** * ipatch_sf2_sample_peek_linked: (skip) * @sample: Sample to get linked sample from * * Get the stereo linked sample from @sample. Like * ipatch_sf2_sample_get_linked() but sample object is not referenced. * This function should only be used if a reference of the sample object * is ensured or only the pointer value is of importance. * * Returns: (transfer none): Linked sample object of sample or %NULL if none. * Remember that a reference is NOT added. */ IpatchSF2Sample * ipatch_sf2_sample_peek_linked(IpatchSF2Sample *sample) { IpatchSF2Sample *linked; g_return_val_if_fail(IPATCH_IS_SF2_SAMPLE(sample), NULL); IPATCH_ITEM_RLOCK(sample); linked = g_weak_ref_get(&sample->linked); // ++ ref linked IPATCH_ITEM_RUNLOCK(sample); if(linked) { g_object_unref(linked); // -- unref linked } return (linked); } /** * ipatch_sf2_sample_set_blank: * @sample: Sample to set to blank sample data * * Set the sample data of a sample item to blank data. */ void ipatch_sf2_sample_set_blank(IpatchSF2Sample *sample) { IpatchSampleData *sampledata; g_return_if_fail(IPATCH_IS_SF2_SAMPLE(sample)); sampledata = ipatch_sample_data_get_blank(); ipatch_item_set_atomic(sample, "sample-data", sampledata, "loop-start", 8, "loop-end", 40, "root-note", 60, "fine-tune", 0, "channel", IPATCH_SF2_SAMPLE_CHANNEL_MONO, "rom", FALSE, "linked-sample", NULL, NULL); g_object_unref(sampledata); } libinstpatch-1.1.6/libinstpatch/IpatchSF2Sample.h000066400000000000000000000114241400263525300217270ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_SAMPLE_H__ #define __IPATCH_SF2_SAMPLE_H__ #include #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchSF2Sample IpatchSF2Sample; typedef struct _IpatchSF2SampleClass IpatchSF2SampleClass; #define IPATCH_TYPE_SF2_SAMPLE (ipatch_sf2_sample_get_type ()) #define IPATCH_SF2_SAMPLE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SF2_SAMPLE, \ IpatchSF2Sample)) #define IPATCH_SF2_SAMPLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SF2_SAMPLE, \ IpatchSF2SampleClass)) #define IPATCH_IS_SF2_SAMPLE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SF2_SAMPLE)) #define IPATCH_IS_SF2_SAMPLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SF2_SAMPLE)) #define IPATCH_SF2_SAMPLE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SF2_SAMPLE, \ IpatchSF2SampleClass)) /* SoundFont sample item */ struct _IpatchSF2Sample { IpatchItem parent_instance; IpatchSampleData *sample_data; /* sample data object */ char *name; /* name of sample */ int rate; /* sample rate */ guint32 loop_start; /* loop start offset (in samples) */ guint32 loop_end; /* loop end offset (in samples, first sample AFTER loop actually) */ guint8 root_note; /* root midi note number */ gint8 fine_tune; /* fine tuning in cents */ guint8 channel; /* IpatchSF2SampleChannel */ GWeakRef linked; /* linked sample pointer or NULL */ }; struct _IpatchSF2SampleClass { IpatchItemClass parent_class; }; /* sampletype flag defines */ /** * IpatchSF2SampleChannel: * @IPATCH_SF2_SAMPLE_CHANNEL_MONO: Mono sample * @IPATCH_SF2_SAMPLE_CHANNEL_LEFT: Left channel of a stereo pair * (#IpatchSF2Sample::linked-sample will be set to the right channel) * @IPATCH_SF2_SAMPLE_CHANNEL_RIGHT: Right channel of a stereo pair * (#IpatchSF2Sample::linked-sample will be set to the left channel) * * Sample channel orientation. */ typedef enum { IPATCH_SF2_SAMPLE_CHANNEL_MONO, IPATCH_SF2_SAMPLE_CHANNEL_LEFT, IPATCH_SF2_SAMPLE_CHANNEL_RIGHT } IpatchSF2SampleChannel; /* sample rate and length constraints */ /** * IPATCH_SF2_SAMPLE_RATE_MIN: (skip) */ #define IPATCH_SF2_SAMPLE_RATE_MIN 400 /* min sample rate (by standard) */ /** * IPATCH_SF2_SAMPLE_RATE_MAX: (skip) */ #define IPATCH_SF2_SAMPLE_RATE_MAX 50000 /* max rate (by the standard) */ /** * IPATCH_SF2_SAMPLE_LENGTH_MIN: (skip) */ #define IPATCH_SF2_SAMPLE_LENGTH_MIN 32 /* min length (by the standard) */ /** * IpatchSF2SampleFlags: * @IPATCH_SF2_SAMPLE_FLAG_ROM: IpatchItem flag for indicating ROM sample */ typedef enum { IPATCH_SF2_SAMPLE_FLAG_ROM = (1 << IPATCH_ITEM_UNUSED_FLAG_SHIFT) } IpatchSF2SampleFlags; /** * IPATCH_SAMPLE_UNUSED_FLAG_SHIFT: (skip) */ /* we reserve flags for ROM flag and 3 for expansion */ #define IPATCH_SF2_SAMPLE_UNUSED_FLAG_SHIFT \ (IPATCH_ITEM_UNUSED_FLAG_SHIFT + 4) GType ipatch_sf2_sample_get_type(void); IpatchSF2Sample *ipatch_sf2_sample_new(void); IpatchSF2Sample *ipatch_sf2_sample_first(IpatchIter *iter); IpatchSF2Sample *ipatch_sf2_sample_next(IpatchIter *iter); void ipatch_sf2_sample_set_name(IpatchSF2Sample *sample, const char *name); char *ipatch_sf2_sample_get_name(IpatchSF2Sample *sample); void ipatch_sf2_sample_set_data(IpatchSF2Sample *sample, IpatchSampleData *sampledata); IpatchSampleData *ipatch_sf2_sample_get_data(IpatchSF2Sample *sample); IpatchSampleData *ipatch_sf2_sample_peek_data(IpatchSF2Sample *sample); void ipatch_sf2_sample_set_linked(IpatchSF2Sample *sample, IpatchSF2Sample *linked); IpatchSF2Sample *ipatch_sf2_sample_get_linked(IpatchSF2Sample *sample); IpatchSF2Sample *ipatch_sf2_sample_peek_linked(IpatchSF2Sample *sample); void ipatch_sf2_sample_set_blank(IpatchSF2Sample *sample); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2VoiceCache.c000066400000000000000000000532351400263525300225000ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2VoiceCache * @short_description: SoundFont voice cache object * @see_also: * @stability: Stable * * This is used for pre-processing instruments into arrays of SoundFont * compatible voices which can then be accessed very quickly without * multi-thread locking or other issues (during synthesis for example). */ #include #include #include #include "IpatchSF2VoiceCache.h" #include "IpatchSF2Mod.h" #include "IpatchTypeProp.h" #include "i18n.h" /* SoundFont voice native sample format */ #define VOICE_SAMPLE_FORMAT \ (IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_MONO | IPATCH_SAMPLE_ENDIAN_HOST) static void ipatch_sf2_voice_cache_class_init(IpatchSF2VoiceCacheClass *klass); static void ipatch_sf2_voice_cache_init(IpatchSF2VoiceCache *cache); static void ipatch_sf2_voice_cache_finalize(GObject *gobject); static gpointer parent_class = NULL; static IpatchSF2Voice def_voice; /* default voice structure */ /* default selection info */ static IpatchSF2VoiceSelInfo default_sel_info[] = { { IPATCH_SF2_VOICE_SEL_NOTE }, { IPATCH_SF2_VOICE_SEL_VELOCITY } }; GType ipatch_sf2_voice_cache_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchSF2VoiceCacheClass), NULL, NULL, (GClassInitFunc)ipatch_sf2_voice_cache_class_init, NULL, NULL, sizeof(IpatchSF2VoiceCache), 0, (GInstanceInitFunc)ipatch_sf2_voice_cache_init, }; item_type = g_type_register_static(G_TYPE_OBJECT, "IpatchSF2VoiceCache", &item_info, 0); /* install voice update function type property */ ipatch_type_install_property (g_param_spec_pointer("sf2voice-update-func", "sf2voice-update-func", "sf2voice-update-func", G_PARAM_READWRITE)); } return (item_type); } static void ipatch_sf2_voice_cache_class_init(IpatchSF2VoiceCacheClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->finalize = ipatch_sf2_voice_cache_finalize; /* initialize default voice structure */ def_voice.sample_data = NULL; def_voice.sample_store = NULL; def_voice.sample_size = 0; def_voice.loop_start = 0; def_voice.loop_end = 0; def_voice.rate = 44100; def_voice.root_note = 60; def_voice.fine_tune = 0; def_voice.reserved = 0; ipatch_sf2_gen_array_init(&def_voice.gen_array, FALSE, FALSE); def_voice.mod_list = NULL; def_voice.range_index = 0; /* init in ipatch_sf2_voice_cache_add_voice */ } static void ipatch_sf2_voice_cache_init(IpatchSF2VoiceCache *cache) { cache->sel_info = g_memdup(default_sel_info, sizeof(default_sel_info)); cache->sel_count = 2; /* default for velocity and note selection */ cache->voices = g_array_new(FALSE, FALSE, sizeof(IpatchSF2Voice)); cache->ranges = NULL; /* initialized later */ cache->default_mods = ipatch_sf2_mod_list_duplicate (ipatch_sf2_mod_list_get_default()); cache->default_loop_type = IPATCH_SAMPLE_LOOP_STANDARD; } static void ipatch_sf2_voice_cache_finalize(GObject *gobject) { IpatchSF2VoiceCache *cache = IPATCH_SF2_VOICE_CACHE(gobject); IpatchSF2Voice *voice; guint i; g_free(cache->sel_info); /*-------- free IpatchSF2Voice field members -------------------------------*/ for(i = 0; i < cache->voices->len; i++) { voice = &g_array_index(cache->voices, IpatchSF2Voice, i); /* running voice_user_data_destroy() function first ensures that the function could expect that any IpatchSF2Voice field member is still valid. This is necessarry if the parameter voice->user_data is a pointer to one of this field. */ if(cache->voice_user_data_destroy && voice->user_data) { cache->voice_user_data_destroy(voice->user_data); } if(voice->sample_data) /* -- unref sample data */ { g_object_unref(voice->sample_data); } if(voice->sample_store) /* -- unref sample store */ { g_object_unref(voice->sample_store); } if(voice->mod_list) /* free modulators */ { ipatch_sf2_mod_list_free(voice->mod_list, TRUE); } } g_array_free(cache->voices, TRUE); /*-------- free IpatchSF2VoiceCache field members -----------------------*/ /* running user_data_destroy() function first ensures that the function could expect that any IpatchSF2VoiceCache field member is still valid. This is necessarry if the parameter cache->user_data is a pointer to one of this field. */ if(cache->user_data_destroy && cache->user_data) { cache->user_data_destroy(cache->user_data); } if(cache->ranges) { g_array_free(cache->ranges, TRUE); } if(cache->default_mods) { ipatch_sf2_mod_list_free(cache->default_mods, TRUE); } if(cache->override_mods) { ipatch_sf2_mod_list_free(cache->override_mods, TRUE); } if(G_OBJECT_CLASS(parent_class)->finalize) { G_OBJECT_CLASS(parent_class)->finalize(gobject); } } /* FIXME-GIR: Voice cache interface isn't currently binding friendly */ /** * ipatch_sf2_voice_cache_new: (skip) * @info: (nullable): Array of selection info structures (length should be @sel_count), use * %NULL for default selection info (MIDI note and velocity) * @sel_count: Count of selection ranges for this cache (ignored if @info is %NULL) * * Create a new SoundFont voice cache object. The @sel_count parameter * defines the number of selection ranges for the cache. Examples of selection * ranges include MIDI note and velocity ranges for a voice. The @info * parameter should be a pointer to an array of selection info structures * which describes each selection type. * * Returns: New SoundFont voice cache with a reference count of 1 which the * caller owns. */ IpatchSF2VoiceCache * ipatch_sf2_voice_cache_new(IpatchSF2VoiceSelInfo *info, int sel_count) { IpatchSF2VoiceCache *cache; g_return_val_if_fail(!info || sel_count > 0, NULL); cache = IPATCH_SF2_VOICE_CACHE(g_object_new(IPATCH_TYPE_SF2_VOICE_CACHE, NULL)); if(info) { g_free(cache->sel_info); cache->sel_info = g_memdup(info, sizeof(IpatchSF2VoiceSelInfo) * sel_count); cache->sel_count = sel_count; } return (cache); } /** * ipatch_sf2_voice_cache_set_default_mods: (skip) * @cache: Voice cache * @mods: (element-type IpatchSF2Mod) (transfer full): SoundFont modulator list * to use as default (used directly) * * Set the default modulator list for the voice cache. Modulator list is used * directly and the allocation of the list is taken over by the voice cache. */ void ipatch_sf2_voice_cache_set_default_mods(IpatchSF2VoiceCache *cache, GSList *mods) { g_return_if_fail(IPATCH_IS_SF2_VOICE_CACHE(cache)); if(cache->default_mods) { ipatch_sf2_mod_list_free(cache->default_mods, TRUE); } cache->default_mods = mods; } /** * ipatch_sf2_voice_cache_set_override_mods: (skip) * @cache: Voice cache * @mods: (element-type IpatchSF2Mod) (transfer full): SoundFont modulator list * which overrides rendered voice modulators (used directly) * * Set the override modulator list for the voice cache. Modulator list is used * directly and the allocation of the list is taken over by the voice cache. * * Since: 1.1.0 */ void ipatch_sf2_voice_cache_set_override_mods(IpatchSF2VoiceCache *cache, GSList *mods) { g_return_if_fail(IPATCH_IS_SF2_VOICE_CACHE(cache)); if(cache->override_mods) { ipatch_sf2_mod_list_free(cache->override_mods, TRUE); } cache->override_mods = mods; } /** * ipatch_sf2_voice_cache_add_voice: (skip) * @cache: Voice cache to create voice for * * Adds a new initialized voice to a SoundFont voice cache. * * Returns: (transfer none): Pointer to new voice which is owned by the @cache. * The sample pointer is set to NULL, generator array is initialized to default * absolute unset values, selection ranges are set to G_MININT-G_MAXINT and * all other fields are initialized to defaults. */ IpatchSF2Voice * ipatch_sf2_voice_cache_add_voice(IpatchSF2VoiceCache *cache) { IpatchSF2Voice *voice; int *ranges; int i; g_return_val_if_fail(IPATCH_IS_SF2_VOICE_CACHE(cache), NULL); /* create range integer array if this is the first voice */ if(!cache->ranges) cache->ranges = g_array_new(FALSE, FALSE, 2 * sizeof(int) * cache->sel_count); g_array_append_val(cache->voices, def_voice); voice = &g_array_index(cache->voices, IpatchSF2Voice, cache->voices->len - 1); voice->range_index = cache->ranges->len * cache->sel_count * 2; /* add selection ranges for this voice to range array */ g_array_set_size(cache->ranges, cache->ranges->len + 1); /* initialize ranges to wildcard range */ ranges = &((int *)(cache->ranges->data))[voice->range_index]; for(i = 0; i < cache->sel_count; i++) { ranges[i * 2] = G_MININT; ranges[i * 2 + 1] = G_MAXINT; } return (voice); } /** * ipatch_sf2_voice_cache_set_voice_range: (skip) * @cache: Voice cache object * @voice: Voice pointer in cache * @sel_index: Selection index * @low: Range low value * @high: Range high value * * Set a voice selection range. Selection ranges are used for selection criteria * such as MIDI velocity and note ranges. */ void ipatch_sf2_voice_cache_set_voice_range(IpatchSF2VoiceCache *cache, IpatchSF2Voice *voice, guint sel_index, int low, int high) { int *rangep; g_return_if_fail(IPATCH_IS_SF2_VOICE_CACHE(cache)); g_return_if_fail(voice != NULL); g_return_if_fail(sel_index < (guint)cache->sel_count); g_return_if_fail(low <= high); rangep = (int *)(cache->ranges->data); rangep[voice->range_index + sel_index * 2] = low; rangep[voice->range_index + sel_index * 2 + 1] = high; } /** * ipatch_sf2_voice_set_sample_data: (skip) * @voice: SoundFont voice structure * @sample_data: Sample data to assign to voice * * Assign sample data to a SoundFont voice. */ void ipatch_sf2_voice_set_sample_data(IpatchSF2Voice *voice, IpatchSampleData *sample_data) { g_return_if_fail(voice != NULL); g_return_if_fail(IPATCH_IS_SAMPLE_DATA(sample_data)); if(voice->sample_data) { g_object_unref(voice->sample_data); } voice->sample_data = g_object_ref(sample_data); /* ++ ref sample data */ if(voice->sample_store) { g_object_unref(voice->sample_store); } voice->sample_store = NULL; voice->sample_size = ipatch_sample_data_get_size(voice->sample_data); } /** * ipatch_sf2_voice_cache_sample_data: (skip) * @voice: SoundFont voice structure * @err: Location to store error info or %NULL to ignore * * Cache an already assigned sample data object of a voice. The sample data is * cached as 16 bit mono native endian format, if not already cached, and the * new cached sample is assigned to the sample_store field. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set) */ gboolean ipatch_sf2_voice_cache_sample_data(IpatchSF2Voice *voice, GError **err) { g_return_val_if_fail(voice != NULL, FALSE); g_return_val_if_fail(!err || !*err, FALSE); g_return_val_if_fail(voice->sample_data != NULL, FALSE); if(voice->sample_store) { g_object_unref(voice->sample_store); } /* FIXME - Do we need channel_map per voice? */ /* ++ ref cached sample store for voice */ voice->sample_store = ipatch_sample_data_get_cache_sample(voice->sample_data, VOICE_SAMPLE_FORMAT, IPATCH_SAMPLE_UNITY_CHANNEL_MAP, err); return (voice->sample_store != NULL); } /** * ipatch_sf2_voice_copy: (skip) * @dest: Destination voice to copy to (initialized to 0s or already valid) * @src: Source voice to copy from * * Copy a source (@src) voice's information to a destination voice (@dest). * Does not copy selection criteria integers in parent #IpatchSF2VoiceCache * objects. */ void ipatch_sf2_voice_copy(IpatchSF2Voice *dest, IpatchSF2Voice *src) { g_return_if_fail(dest != NULL); g_return_if_fail(src != NULL); if(dest->sample_data) { g_object_unref(dest->sample_data); } if(dest->sample_store) { g_object_unref(dest->sample_store); } if(src->sample_data) { dest->sample_data = g_object_ref(src->sample_data); } else { dest->sample_data = NULL; } if(src->sample_store) { dest->sample_store = g_object_ref(src->sample_store); } else { dest->sample_store = NULL; } dest->sample_size = src->sample_size; dest->loop_start = src->loop_start; dest->loop_end = src->loop_end; dest->rate = src->rate; dest->root_note = src->root_note; dest->fine_tune = src->fine_tune; dest->reserved = src->reserved; dest->gen_array = src->gen_array; dest->mod_list = ipatch_sf2_mod_list_duplicate(src->mod_list); } /** * ipatch_sf2_voice_cache_optimize: (skip) * @cache: SoundFont voice cache * * Can be called after all voices have been added to a voice cache. * Will optimize voice cache for use with ipatch_sf2_voice_cache_select(). * NOTE: Currently does nothing, but will in the future.. */ void ipatch_sf2_voice_cache_optimize(IpatchSF2VoiceCache *cache) { /* FIXME */ } /** * ipatch_sf2_voice_cache_select: (skip) * @cache: SoundFont voice cache * @select_values: Array of select values which must be the same length as * the voice cache was initialized with. Each selection value is tested * against each voice's selection ranges (use #IPATCH_SF2_VOICE_SEL_WILDCARD * as a wildcard selection value). * @index_array: Voice indexes are stored in this array. * @max_indexes: Maximum number of voices to match. @index_array should be * at least this value in size. * * Stores pointers to voices matching @select_values criteria. * * Returns: Number of indexes stored to @index_array. */ int ipatch_sf2_voice_cache_select(IpatchSF2VoiceCache *cache, int *select_values, guint16 *index_array, guint16 max_indexes) { IpatchSF2Voice *voice; guint16 *indexp = index_array; GArray *voices; int *range_array; int i, count, scount, sv, si, ri; int matches; g_return_val_if_fail(IPATCH_IS_SF2_VOICE_CACHE(cache), 0); g_return_val_if_fail(select_values != NULL, 0); g_return_val_if_fail(index_array != NULL, 0); g_return_val_if_fail(max_indexes > 0, 0); /* OPTME - This could be optimized, currently just iterates over array */ count = cache->voices->len; voices = cache->voices; scount = cache->sel_count; if(!cache->ranges) /* no ranges means no voices, return */ { return (0); } range_array = (int *)(cache->ranges->data); /* array will not change */ /* loop over all voices */ for(i = 0, matches = 0; i < count && matches < max_indexes; i++) { voice = &g_array_index(voices, IpatchSF2Voice, i); /* loop over selection ranges */ for(si = 0, ri = voice->range_index; si < scount; si++, ri += 2) { sv = select_values[si]; /* break out if no match (not -1 and not in range) */ if(sv != -1 && !(sv >= range_array[ri] && sv <= range_array[ri + 1])) { break; } } if(si == scount) /* all range selection criteria matched? */ { *indexp = i; /* add voice index to index array */ indexp++; matches++; } } return (matches); } /** * ipatch_sf2_voice_cache_updates: (skip) * @cache: Voice cache to get updates for * @select_values: The voice selection criteria to use, should be the same * number of select values as in @cache * @cache_item: Original item @cache was created from * @item: Object for which a property changed (only really makes sense if it is * a dependent item of @cache_item). * @pspec: Parameter specification of property which changed * @value: The new value of the property * @updates: Output array to store updates to * @max_updates: Size of @updates array (max possible update values). * * This function is used to re-calculate SoundFont effect generators for a * single object property change. Useful for real time effect changes. * * Returns: Number of updates stored to @updates array or -1 if not handled * (no handler for the given @item). Will be 0 if no updates required. */ int ipatch_sf2_voice_cache_update(IpatchSF2VoiceCache *cache, int *select_values, GObject *cache_item, GObject *item, GParamSpec *pspec, const GValue *value, IpatchSF2VoiceUpdate *updates, guint max_updates) { IpatchSF2VoiceCacheUpdateHandler handler; g_return_val_if_fail(IPATCH_IS_SF2_VOICE_CACHE(cache), -1); g_return_val_if_fail(select_values != NULL, -1); g_return_val_if_fail(G_IS_OBJECT(cache_item), -1); g_return_val_if_fail(G_IS_OBJECT(item), -1); g_return_val_if_fail(G_IS_PARAM_SPEC(pspec), -1); g_return_val_if_fail(G_IS_VALUE(value), -1); g_return_val_if_fail(updates != NULL, -1); ipatch_type_get(G_OBJECT_TYPE(cache_item), "sf2voice-update-func", &handler, NULL); if(!handler) { return (-1); } if(max_updates == 0) { return (0); } return handler(cache, select_values, cache_item, item, pspec, value, updates, max_updates); } #ifdef IPATCH_DEBUG /** * ipatch_sf2_voice_cache_dump: (skip) * * Debugging function to dump a voice cache to stdout */ void ipatch_sf2_voice_cache_dump(IpatchSF2VoiceCache *cache, int start, int count) { IpatchSF2Voice *voice; char *selnames[] = { "Note", "Vel", "AftTch", "CC" }; int *range; int i, si; g_return_if_fail(IPATCH_IS_SF2_VOICE_CACHE(cache)); if(start == 0) { printf("Voice cache selection criteria:\n"); for(i = 0; i < cache->sel_count; i++) { switch(cache->sel_info[i].type) { case IPATCH_SF2_VOICE_SEL_NOTE: printf("%d: Note\n", i); break; case IPATCH_SF2_VOICE_SEL_VELOCITY: printf("%d: Velocity\n", i); break; case IPATCH_SF2_VOICE_SEL_AFTER_TOUCH: printf("%d: After touch\n", i); break; case IPATCH_SF2_VOICE_SEL_MIDI_CC: printf("%d: CC %d\n", i, cache->sel_info[i].param1); break; } } } for(i = start; i < start + count && i < cache->voices->len; i++) { voice = &g_array_index(cache->voices, IpatchSF2Voice, i); printf("%d (S:%d,SD:%p,SS:%p) L:%d-%d R:%d RN:%d T:%d\n ", i, voice->sample_size, voice->sample_data, voice->sample_store, voice->loop_start, voice->loop_end, voice->rate, voice->root_note, voice->fine_tune); range = &g_array_index(cache->ranges, int, voice->range_index); for(si = 0; si < cache->sel_count; si++) { printf(" %s: %d-%d", selnames[cache->sel_info[si].type], range[si * 2], range[si * 2 + 1]); } printf("\n"); } } /** * ipatch_sf2_voice_cache_dump_select: (skip) */ void ipatch_sf2_voice_cache_dump_select(IpatchSF2VoiceCache *cache, ...) { IpatchSF2Voice *voice; va_list args; char *selnames[] = { "Note", "Vel", "AftTch", "CC" }; guint16 indexes[256]; int selvals[8]; int *range; int count, i, vindex, si; va_start(args, cache); for(i = 0; i < cache->sel_count; i++) { selvals[i] = va_arg(args, int); } va_end(args); count = ipatch_sf2_voice_cache_select(cache, selvals, indexes, G_N_ELEMENTS(indexes)); printf("%d voices matched:\n", count); for(i = 0; i < count; i++) { vindex = indexes[i]; voice = &g_array_index(cache->voices, IpatchSF2Voice, vindex); printf("%d (S:%d,SD:%p,SS:%p) L:%d-%d R:%d RN:%d T:%d\n ", vindex, voice->sample_size, voice->sample_data, voice->sample_store, voice->loop_start, voice->loop_end, voice->rate, voice->root_note, voice->fine_tune); range = &g_array_index(cache->ranges, int, voice->range_index); for(si = 0; si < cache->sel_count; si++) { printf(" %s: %d-%d", selnames[cache->sel_info[si].type], range[si * 2], range[si * 2 + 1]); } printf("\n"); } } #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2VoiceCache.h000066400000000000000000000212141400263525300224750ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_VOICE_CACHE_H__ #define __IPATCH_SF2_VOICE_CACHE_H__ #include #include #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchSF2VoiceCache IpatchSF2VoiceCache; typedef struct _IpatchSF2VoiceCacheClass IpatchSF2VoiceCacheClass; typedef struct _IpatchSF2Voice IpatchSF2Voice; typedef struct _IpatchSF2VoiceUpdate IpatchSF2VoiceUpdate; typedef struct _IpatchSF2VoiceSelInfo IpatchSF2VoiceSelInfo; #define IPATCH_TYPE_SF2_VOICE_CACHE (ipatch_sf2_voice_cache_get_type ()) #define IPATCH_SF2_VOICE_CACHE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SF2_VOICE_CACHE, \ IpatchSF2VoiceCache)) #define IPATCH_SF2_VOICE_CACHE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SF2_VOICE_CACHE, \ IpatchSF2VoiceCacheClass)) #define IPATCH_IS_SF2_VOICE_CACHE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SF2_VOICE_CACHE)) #define IPATCH_IS_SF2_VOICE_CACHE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SF2_VOICE_CACHE)) /** * IpatchSF2VoiceCacheItemFunc: * @cache: Voice cache * @item: Item which voice cache is dependent on * * A callback function type which is called during voice cache population * for each item which the voice cache is dependent on. This can be useful * for determining when a voice cache needs to be updated or for real time * effects. */ typedef void (*IpatchSF2VoiceCacheItemFunc)(IpatchSF2VoiceCache *cache, GObject *item); /* SoundFont voice cache object */ struct _IpatchSF2VoiceCache { GObject parent_instance; IpatchSF2VoiceSelInfo *sel_info; /* array of selection criteria info */ int sel_count; /* count of selection ranges per voice (integer pairs) */ GArray *voices; /* array of IpatchSF2Voice structures */ GArray *ranges; /* array of selection integer pairs for each voice */ GSList *default_mods; /* default modulators */ /* default loop type which can be used for objects that don't define it */ IpatchSampleLoopType default_loop_type; /* dependent item callback function */ IpatchSF2VoiceCacheItemFunc item_func; gpointer item_func_data; /* user defined data used by item_func */ /* IpatchSF2VoiceCache user defined */ gpointer user_data; /* Arbitrary data defined by IpatchSF2VoiceCache user */ GDestroyNotify user_data_destroy; /* Optional callback to destroy user_data */ GDestroyNotify voice_user_data_destroy; /* Optional callback to destroy user_data in each voice */ /* Added with version 1.1.0 */ GSList *override_mods; /* override modulators (added with libInstPatch version 1.1.0) */ }; struct _IpatchSF2VoiceCacheClass { GObjectClass parent_class; }; /* a SoundFont voice */ struct _IpatchSF2Voice { /* Set by SF2VoiceCache converter via ipatch_sf2_voice_set_sample_data() */ IpatchSampleData *sample_data; /* sample data for voice */ IpatchSampleStore *sample_store; /* Cached store */ guint32 sample_size; /* size of sample in frames */ /* Set by SF2VoiceCache converter */ guint32 loop_start; /* loop start offset (in samples) */ guint32 loop_end; /* loop end offset (in samples, 1st sample after loop) */ guint32 rate; /* sample rate */ guint8 root_note; /* MIDI root note of sample */ gint8 fine_tune; /* fine tune (in cents, -99 - 99) */ guint16 reserved; /* reserved (should be 0) */ IpatchSF2GenArray gen_array; /* generator effect values */ GSList *mod_list; /* modulator list */ /* IpatchSF2VoiceCache user defined */ gpointer user_data; /* Arbitrary data defined by IpatchSF2VoiceCache user */ /* Set internally */ int range_index; /* index in ranges array (int *) to first selection range */ }; /* a voice parameter update (used for realtime effects) */ struct _IpatchSF2VoiceUpdate { guint16 voice; /* index of voice with parameter to update */ union /* new value for parameter */ { gint16 ival; guint16 uval; }; guint8 genid; /* if type == IPATCH_SF2_VOICE_UPDATE_GEN: id of gen */ guint8 reserved[3]; /* padding to 4 bytes */ }; /** * IpatchSF2VoiceCacheUpdateHandler: * @cache: Voice cache to get updates for * @select_values: The voice selection criteria to use, should be the same * number of select values as in @cache * @cache_item: Original item @cache was created from * @item: Object for which a property changed * @pspec: Parameter specification of property which changed * @value: The new value of the property * @updates: Output array to store updates to * @max_updates: Size of @updates array (max possible update values). * * Function prototype used to re-calculate SoundFont effect generators for a * single object property change. Useful for real time effect changes. * * Returns: Should return number of updates stored to @updates array. * Will be 0 if no updates required. */ typedef int (*IpatchSF2VoiceCacheUpdateHandler)(IpatchSF2VoiceCache *cache, int *select_values, GObject *cache_item, GObject *item, GParamSpec *pspec, const GValue *value, IpatchSF2VoiceUpdate *updates, guint max_updates); /* voice selection type */ typedef enum { IPATCH_SF2_VOICE_SEL_NOTE, /* MIDI note range */ IPATCH_SF2_VOICE_SEL_VELOCITY, /* MIDI velocity range */ IPATCH_SF2_VOICE_SEL_AFTER_TOUCH, /* MIDI aftertouch range */ IPATCH_SF2_VOICE_SEL_MIDI_CC /* MIDI custom controller (param1: ctrlnum) */ } IpatchSF2VoiceSelType; /* selection info structure */ struct _IpatchSF2VoiceSelInfo { IpatchSF2VoiceSelType type; int param1; int param2; /* currently not used */ }; /* maximum allowed voice selection criteria (MIDI note, velocity, etc) */ #define IPATCH_SF2_VOICE_CACHE_MAX_SEL_VALUES 32 /* value used for wildcard selection */ #define IPATCH_SF2_VOICE_SEL_WILDCARD (G_MININT) /* For voice cache propagation methods to declare dependent items */ #define ipatch_sf2_voice_cache_declare_item(cache, item) \ if (cache->item_func) cache->item_func (cache, item) /* Macro for retrieving a voice pointer from a cache */ #define IPATCH_SF2_VOICE_CACHE_GET_VOICE(cache, index) \ (&g_array_index (cache->voices, IpatchSF2Voice, index)) GType ipatch_sf2_voice_cache_get_type(void); IpatchSF2VoiceCache *ipatch_sf2_voice_cache_new(IpatchSF2VoiceSelInfo *info, int sel_count); void ipatch_sf2_voice_cache_set_default_mods(IpatchSF2VoiceCache *cache, GSList *mods); void ipatch_sf2_voice_cache_set_override_mods(IpatchSF2VoiceCache *cache, GSList *mods); IpatchSF2Voice *ipatch_sf2_voice_cache_add_voice(IpatchSF2VoiceCache *cache); void ipatch_sf2_voice_cache_set_voice_range(IpatchSF2VoiceCache *cache, IpatchSF2Voice *voice, guint sel_index, int low, int high); void ipatch_sf2_voice_set_sample_data(IpatchSF2Voice *voice, IpatchSampleData *sample_data); gboolean ipatch_sf2_voice_cache_sample_data(IpatchSF2Voice *voice, GError **err); void ipatch_sf2_voice_copy(IpatchSF2Voice *dest, IpatchSF2Voice *src); void ipatch_sf2_voice_cache_optimize(IpatchSF2VoiceCache *cache); int ipatch_sf2_voice_cache_select(IpatchSF2VoiceCache *cache, int *select_values, guint16 *index_array, guint16 max_indexes); int ipatch_sf2_voice_cache_update(IpatchSF2VoiceCache *cache, int *select_values, GObject *cache_item, GObject *item, GParamSpec *pspec, const GValue *value, IpatchSF2VoiceUpdate *updates, guint max_updates); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2VoiceCache_DLS.c000066400000000000000000000213041400263525300231720ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #include #include #include #include #include "IpatchSF2VoiceCache_DLS.h" #include "IpatchConverter.h" #include "IpatchConverterSF2VoiceCache.h" #include "IpatchConverter_priv.h" #include "IpatchDLS2Inst.h" #include "IpatchDLS2Region.h" #include "IpatchDLS2Sample.h" #include "IpatchSF2VoiceCache.h" #include "IpatchSample.h" /** * _ipatch_sf2_voice_cache_init_DLS: (skip) */ void _ipatch_sf2_voice_cache_init_DLS(void) { g_type_class_ref(IPATCH_TYPE_CONVERTER_DLS2_INST_TO_SF2_VOICE_CACHE); g_type_class_ref(IPATCH_TYPE_CONVERTER_DLS2_REGION_TO_SF2_VOICE_CACHE); g_type_class_ref(IPATCH_TYPE_CONVERTER_DLS2_SAMPLE_TO_SF2_VOICE_CACHE); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_DLS2_INST_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_DLS2_INST, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_DLS2_REGION_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_DLS2_REGION, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_DLS2_SAMPLE_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_DLS2_SAMPLE, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); } static gboolean _dls2_inst_to_sf2_voice_cache_convert(IpatchConverter *converter, GError **err) { IpatchDLS2Inst *inst; IpatchSF2VoiceCache *cache; IpatchSF2Voice *voice; IpatchDLS2Region *region; IpatchDLS2Sample *sample; IpatchDLS2SampleInfo *sample_info; IpatchItem *solo_item; GObject *obj; GSList *p; int looptype; obj = IPATCH_CONVERTER_INPUT(converter); cache = IPATCH_SF2_VOICE_CACHE(IPATCH_CONVERTER_OUTPUT(converter)); solo_item = ((IpatchConverterSF2VoiceCache *)converter)->solo_item; /* check if its an instrument or a region */ if(IPATCH_IS_DLS2_REGION(obj)) /* ++ ref parent instrument */ { inst = IPATCH_DLS2_INST(ipatch_item_get_parent(IPATCH_ITEM(obj))); } else { inst = IPATCH_DLS2_INST(obj); } ipatch_sf2_voice_cache_declare_item(cache, (GObject *)inst); IPATCH_ITEM_RLOCK(inst); /* ++ LOCK instrument */ for(p = inst->regions; p; p = p->next) { region = (IpatchDLS2Region *)(p->data); if(solo_item && (IpatchItem *)region != solo_item) { continue; } ipatch_sf2_voice_cache_declare_item(cache, (GObject *)region); voice = ipatch_sf2_voice_cache_add_voice(cache); IPATCH_ITEM_RLOCK(region); /* ++ LOCK region */ /* convert DLS parameters to SoundFont generator array and modulators */ // ipatch_dls2_region_load_sf2_gen_mods (region, &voice->gen_array, // &voice->mod_list); voice->mod_list = ipatch_sf2_mod_list_override(cache->default_mods, cache->override_mods, TRUE); /* set MIDI note and velocity ranges */ ipatch_sf2_voice_cache_set_voice_range(cache, voice, 0, region->note_range_low, region->note_range_high); ipatch_sf2_voice_cache_set_voice_range(cache, voice, 1, region->velocity_range_low, region->velocity_range_high); sample = (IpatchDLS2Sample *)(region->sample); ipatch_sf2_voice_cache_declare_item(cache, (GObject *)sample); ipatch_sf2_voice_set_sample_data(voice, sample->sample_data); voice->rate = sample->rate; if(region->sample_info) { sample_info = region->sample_info; } else { sample_info = sample->sample_info; } if(sample_info) { voice->loop_start = sample_info->loop_start; voice->loop_end = sample_info->loop_end; voice->root_note = sample_info->root_note; voice->fine_tune = (guint8)sample_info->fine_tune; switch(sample_info->options & IPATCH_DLS2_SAMPLE_LOOP_MASK) { case IPATCH_SAMPLE_LOOP_NONE: looptype = IPATCH_SF2_GEN_SAMPLE_MODE_NOLOOP; break; case IPATCH_SAMPLE_LOOP_RELEASE: looptype = IPATCH_SF2_GEN_SAMPLE_MODE_LOOP_RELEASE; break; default: /* default to standard loop */ looptype = IPATCH_SF2_GEN_SAMPLE_MODE_LOOP; break; } /* set loop mode */ voice->gen_array.values[IPATCH_SF2_GEN_SAMPLE_MODES].sword = looptype; IPATCH_SF2_GEN_ARRAY_SET_FLAG(&voice->gen_array, IPATCH_SF2_GEN_SAMPLE_MODES); } IPATCH_ITEM_RUNLOCK(region); /* -- UNLOCK region */ } IPATCH_ITEM_RUNLOCK(inst); /* -- UNLOCK instrument */ /* if convert object was region, unref parent instrument */ if((void *)obj != (void *)inst) { g_object_unref(inst); /* -- unref parent instrument */ } return (TRUE); } /* use the instrument converter for regions also */ #define _dls2_region_to_sf2_voice_cache_convert \ _dls2_inst_to_sf2_voice_cache_convert /** * _dls2_sample_to_sf2_voice_cache_convert: (skip) * * DLS2Sample voice cache converter - Not declared static, since used by * IpatchSF2VoiceCache_Gig.c */ gboolean _dls2_sample_to_sf2_voice_cache_convert(IpatchConverter *converter, GError **err) { IpatchDLS2Sample *sample; IpatchSF2VoiceCache *cache; IpatchSF2Voice *voice; IpatchSF2GenAmount *amt; int loopmode; sample = IPATCH_DLS2_SAMPLE(IPATCH_CONVERTER_INPUT(converter)); cache = IPATCH_SF2_VOICE_CACHE(IPATCH_CONVERTER_OUTPUT(converter)); ipatch_sf2_voice_cache_declare_item(cache, (GObject *)sample); voice = ipatch_sf2_voice_cache_add_voice(cache); voice->mod_list = ipatch_sf2_mod_list_duplicate(cache->default_mods); /* set MIDI note and velocity ranges */ amt = &voice->gen_array.values[IPATCH_SF2_GEN_NOTE_RANGE]; ipatch_sf2_voice_cache_set_voice_range(cache, voice, 0, amt->range.low, amt->range.high); amt = &voice->gen_array.values[IPATCH_SF2_GEN_VELOCITY_RANGE]; ipatch_sf2_voice_cache_set_voice_range(cache, voice, 1, amt->range.low, amt->range.high); voice->mod_list = ipatch_sf2_mod_list_override(cache->default_mods, cache->override_mods, TRUE); ipatch_sf2_voice_set_sample_data(voice, sample->sample_data); voice->rate = sample->rate; if(sample->sample_info) { voice->loop_start = sample->sample_info->loop_start; voice->loop_end = sample->sample_info->loop_end; voice->root_note = sample->sample_info->root_note; voice->fine_tune = (guint8)sample->sample_info->fine_tune; switch(sample->sample_info->options & IPATCH_DLS2_SAMPLE_LOOP_MASK) { case IPATCH_SAMPLE_LOOP_NONE: loopmode = IPATCH_SF2_GEN_SAMPLE_MODE_NOLOOP; break; case IPATCH_SAMPLE_LOOP_RELEASE: loopmode = IPATCH_SF2_GEN_SAMPLE_MODE_LOOP_RELEASE; break; default: /* default to standard loop */ loopmode = IPATCH_SF2_GEN_SAMPLE_MODE_LOOP; break; } /* set loop mode */ voice->gen_array.values[IPATCH_SF2_GEN_SAMPLE_MODES].sword = loopmode; IPATCH_SF2_GEN_ARRAY_SET_FLAG(&voice->gen_array, IPATCH_SF2_GEN_SAMPLE_MODES); } return (TRUE); } CONVERTER_CLASS_INIT(dls2_inst_to_sf2_voice_cache) CONVERTER_CLASS_INIT(dls2_region_to_sf2_voice_cache) CONVERTER_CLASS_INIT(dls2_sample_to_sf2_voice_cache) CONVERTER_SF2_VOICE_CACHE_GET_TYPE(dls2_inst_to_sf2_voice_cache, DLS2InstToSF2VoiceCache) CONVERTER_SF2_VOICE_CACHE_GET_TYPE(dls2_region_to_sf2_voice_cache, DLS2RegionToSF2VoiceCache) CONVERTER_SF2_VOICE_CACHE_GET_TYPE(dls2_sample_to_sf2_voice_cache, DLS2SampleToSF2VoiceCache) libinstpatch-1.1.6/libinstpatch/IpatchSF2VoiceCache_DLS.h000066400000000000000000000041531400263525300232020ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_VOICE_CACHE_DLS_H__ #define __IPATCH_SF2_VOICE_CACHE_DLS_H__ #include #include #include #include typedef IpatchConverterSF2VoiceCache IpatchConverterDLS2InstToSF2VoiceCache; typedef IpatchConverterSF2VoiceCacheClass IpatchConverterDLS2InstToSF2VoiceCacheClass; typedef IpatchConverterSF2VoiceCache IpatchConverterDLS2RegionToSF2VoiceCache; typedef IpatchConverterSF2VoiceCacheClass IpatchConverterDLS2RegionToSF2VoiceCacheClass; typedef IpatchConverterSF2VoiceCache IpatchConverterDLS2SampleToSF2VoiceCache; typedef IpatchConverterSF2VoiceCacheClass IpatchConverterDLS2SampleToSF2VoiceCacheClass; #define IPATCH_TYPE_CONVERTER_DLS2_INST_TO_SF2_VOICE_CACHE \ (ipatch_converter_dls2_inst_to_sf2_voice_cache_get_type ()) #define IPATCH_TYPE_CONVERTER_DLS2_REGION_TO_SF2_VOICE_CACHE \ (ipatch_converter_dls2_region_to_sf2_voice_cache_get_type ()) #define IPATCH_TYPE_CONVERTER_DLS2_SAMPLE_TO_SF2_VOICE_CACHE \ (ipatch_converter_dls2_sample_to_sf2_voice_cache_get_type ()) GType ipatch_converter_dls2_inst_to_sf2_voice_cache_get_type(void); GType ipatch_converter_dls2_region_to_sf2_voice_cache_get_type(void); GType ipatch_converter_dls2_sample_to_sf2_voice_cache_get_type(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2VoiceCache_Gig.c000066400000000000000000000247401400263525300232650ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2VoiceCache_Gig * @short_description: Voice cache converters for GigaSampler object types * @see_also: #IpatchSF2VoiceCache * @stability: Stable */ #include #include #include #include "IpatchSF2VoiceCache_Gig.h" #include "IpatchSF2VoiceCache.h" #include "IpatchConverter_priv.h" #include "IpatchConverterSF2VoiceCache.h" #include "IpatchDLS2Inst.h" #include "IpatchDLS2Region.h" #include "IpatchDLS2Sample.h" #include "IpatchSF2Mod.h" #include "IpatchGig.h" #include "IpatchGigInst.h" #include "IpatchGigRegion.h" #include "IpatchGigEffects.h" #include "IpatchSample.h" // In IpatchSF2VoiceCache_DLS.c gboolean _dls2_sample_to_sf2_voice_cache_convert(IpatchConverter *converter, GError **err); /** * _ipatch_sf2_voice_cache_init_gig: (skip) */ void _ipatch_sf2_voice_cache_init_gig(void) { g_type_class_ref(IPATCH_TYPE_CONVERTER_GIG_INST_TO_SF2_VOICE_CACHE); g_type_class_ref(IPATCH_TYPE_CONVERTER_GIG_SAMPLE_TO_SF2_VOICE_CACHE); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_GIG_INST_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_GIG_INST, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_GIG_SAMPLE_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_GIG_SAMPLE, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); } static gboolean _gig_inst_to_sf2_voice_cache_convert(IpatchConverter *converter, GError **err) { IpatchSF2VoiceCache *cache; IpatchSF2Voice *voice; IpatchDLS2Inst *inst; IpatchGigRegion *region; IpatchDLS2Region *dls_region; IpatchGigDimension *dimension; IpatchGigSubRegion *sub_region; IpatchDLS2Sample *sample; IpatchDLS2SampleInfo *sample_info; IpatchSF2VoiceSelInfo sel_info[IPATCH_SF2_VOICE_CACHE_MAX_SEL_VALUES]; IpatchSF2VoiceSelInfo *infop; GHashTable *sel_index_hash; /* dimension type -> selection index */ int sel_count = 1; /* 1 for note selection criteria */ int dim_type; int index, sub_count; int i, i2, v; GSList *p; sel_index_hash = g_hash_table_new(NULL, NULL); cache = IPATCH_SF2_VOICE_CACHE(IPATCH_CONVERTER_OUTPUT(converter)); inst = IPATCH_DLS2_INST(IPATCH_CONVERTER_INPUT(converter)); ipatch_sf2_voice_cache_declare_item(cache, (GObject *)inst); sel_info[0].type = IPATCH_SF2_VOICE_SEL_NOTE; /* always have note selection */ infop = &sel_info[1]; IPATCH_ITEM_RLOCK(inst); /* ++ LOCK instrument */ /* determine all selection criteria types */ p = inst->regions; for(; p; p = p->next) /* loop over DLS regions */ { region = (IpatchGigRegion *)(p->data); ipatch_sf2_voice_cache_declare_item(cache, (GObject *)region); /* NOTE: Dimensions and sub regions share the same mutex as region */ IPATCH_ITEM_RLOCK(region); /* ++ LOCK region */ for(i = 0; i < region->dimension_count; i++) /* loop on dimensions */ { ipatch_sf2_voice_cache_declare_item(cache, (GObject *)(region->dimensions[i])); dim_type = region->dimensions[i]->type; /* channel region types don't entail selection criteria, but rather channel audio routing */ if(dim_type == IPATCH_GIG_DIMENSION_CHANNEL) { continue; } /* already added this region selection type? */ if(g_hash_table_lookup(sel_index_hash, GINT_TO_POINTER(dim_type))) { continue; } infop->type = -1; switch(dim_type) { case IPATCH_GIG_DIMENSION_VELOCITY: infop->type = IPATCH_SF2_VOICE_SEL_VELOCITY; break; case IPATCH_GIG_DIMENSION_AFTER_TOUCH: infop->type = IPATCH_SF2_VOICE_SEL_AFTER_TOUCH; break; case IPATCH_GIG_DIMENSION_RELEASE_TRIG: break; case IPATCH_GIG_DIMENSION_KEYBOARD: break; case IPATCH_GIG_DIMENSION_ROUND_ROBIN: break; case IPATCH_GIG_DIMENSION_RANDOM: break; default: if(dim_type < 0x80) { infop->type = IPATCH_SF2_VOICE_SEL_MIDI_CC; infop->param1 = dim_type; /* type is MIDI CC # */ } break; } /* advance if selection criteria stored */ if(infop->type != -1) { /* add to dim_type -> index hash + 1 (sel_count already + 1) */ g_hash_table_insert(sel_index_hash, GINT_TO_POINTER(dim_type), GINT_TO_POINTER(sel_count)); infop++; sel_count++; } else { g_warning("Unhandled Gig region type %d", dim_type); } /* max number of selection types reached? */ if(infop == &sel_info[IPATCH_SF2_VOICE_CACHE_MAX_SEL_VALUES]) { g_warning("Max voice selection types reached!"); break; } } /* dimension loop */ IPATCH_ITEM_RUNLOCK(region); /* -- UNLOCK region */ } /* region loop */ p = inst->regions; for(; p; p = p->next) /* loop over DLS regions */ { region = (IpatchGigRegion *)(p->data); dls_region = (IpatchDLS2Region *)region; IPATCH_ITEM_RLOCK(region); /* ++ LOCK region */ /* loop over all sub regions */ for(i = 0; i < region->sub_region_count; i++) { sub_region = region->sub_regions[i]; ipatch_sf2_voice_cache_declare_item(cache, (GObject *)sub_region); voice = ipatch_sf2_voice_cache_add_voice(cache); /* convert GigaSampler effects to SoundFont 2 generator array */ ipatch_gig_effects_to_gen_array(&sub_region->effects, &voice->gen_array); /* set note range */ ipatch_sf2_voice_cache_set_voice_range(cache, voice, 0, dls_region->note_range_low, dls_region->note_range_high); /* loop over dimension splits and set selection ranges */ for(i2 = 0; i2 < region->dimension_count; i2++) { dimension = region->dimensions[i2]; dim_type = dimension->type; /* find selection info index for gig dimension type */ index = GPOINTER_TO_INT(g_hash_table_lookup(sel_index_hash, GINT_TO_POINTER(dim_type))); if(!index) { continue; /* not handled (FIXME) */ } /* split index for this dimension */ v = (i & dimension->split_mask) >> dimension->split_shift; /* dimension sub region count */ sub_count = (1 << dimension->split_count); /* set range based on dimension split index */ ipatch_sf2_voice_cache_set_voice_range(cache, voice, index, 128 * v / sub_count, 128 * (v + 1) / sub_count - 1); } voice->mod_list = ipatch_sf2_mod_list_override(cache->default_mods, cache->override_mods, TRUE); sample = (IpatchDLS2Sample *)(sub_region->sample); ipatch_sf2_voice_cache_declare_item(cache, (GObject *)sample); if(sub_region->sample_info) { sample_info = sub_region->sample_info; } else { sample_info = sample->sample_info; } /* FIXME - what about stereo routing? */ /* Assign sample */ ipatch_sf2_voice_set_sample_data(voice, sample->sample_data); /* copy sample parameters */ voice->rate = sample->rate; if(sample_info) { voice->loop_start = sample_info->loop_start; voice->loop_end = sample_info->loop_end; voice->root_note = sample_info->root_note; voice->fine_tune = (guint8)sample_info->fine_tune; switch(sample_info->options & IPATCH_DLS2_SAMPLE_LOOP_MASK) { case IPATCH_SAMPLE_LOOP_NONE: v = IPATCH_SF2_GEN_SAMPLE_MODE_NOLOOP; break; case IPATCH_SAMPLE_LOOP_RELEASE: v = IPATCH_SF2_GEN_SAMPLE_MODE_LOOP_RELEASE; break; default: /* default to standard loop */ v = IPATCH_SF2_GEN_SAMPLE_MODE_LOOP; break; } /* set loop mode */ voice->gen_array.values[IPATCH_SF2_GEN_SAMPLE_MODES].sword = v; IPATCH_SF2_GEN_ARRAY_SET_FLAG(&voice->gen_array, IPATCH_SF2_GEN_SAMPLE_MODES); } } // sub region loop IPATCH_ITEM_RUNLOCK(region); /* -- UNLOCK region */ } // region loop IPATCH_ITEM_RUNLOCK(inst); /* -- UNLOCK instrument */ g_hash_table_destroy(sel_index_hash); return (TRUE); } /* use DLS sample converter function for Gig samples */ #define _gig_sample_to_sf2_voice_cache_convert \ _dls2_sample_to_sf2_voice_cache_convert CONVERTER_CLASS_INIT(gig_inst_to_sf2_voice_cache) CONVERTER_CLASS_INIT(gig_sample_to_sf2_voice_cache) CONVERTER_SF2_VOICE_CACHE_GET_TYPE(gig_inst_to_sf2_voice_cache, GigInstToSF2VoiceCache) CONVERTER_SF2_VOICE_CACHE_GET_TYPE(gig_sample_to_sf2_voice_cache, GigSampleToSF2VoiceCache) libinstpatch-1.1.6/libinstpatch/IpatchSF2VoiceCache_Gig.h000066400000000000000000000033641400263525300232710ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_VOICE_CACHE_GIG_H__ #define __IPATCH_SF2_VOICE_CACHE_GIG_H__ #include #include #include #include typedef IpatchConverterSF2VoiceCache IpatchConverterGigInstToSF2VoiceCache; typedef IpatchConverterSF2VoiceCacheClass IpatchConverterGigInstToSF2VoiceCacheClass; typedef IpatchConverterSF2VoiceCache IpatchConverterGigSampleToSF2VoiceCache; typedef IpatchConverterSF2VoiceCacheClass IpatchConverterGigSampleToSF2VoiceCacheClass; #define IPATCH_TYPE_CONVERTER_GIG_INST_TO_SF2_VOICE_CACHE \ (ipatch_converter_gig_inst_to_sf2_voice_cache_get_type ()) #define IPATCH_TYPE_CONVERTER_GIG_SAMPLE_TO_SF2_VOICE_CACHE \ (ipatch_converter_gig_sample_to_sf2_voice_cache_get_type ()) GType ipatch_converter_gig_inst_to_sf2_voice_cache_get_type(void); GType ipatch_converter_gig_sample_to_sf2_voice_cache_get_type(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2VoiceCache_SF2.c000066400000000000000000000374071400263525300231550ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #include #include #include #include #include "IpatchSF2VoiceCache_SF2.h" #include "IpatchConverter.h" #include "IpatchConverterSF2VoiceCache.h" #include "IpatchConverter_priv.h" #include "IpatchSF2Inst.h" #include "IpatchSF2IZone.h" #include "IpatchSF2Preset.h" #include "IpatchSF2PZone.h" #include "IpatchSF2Sample.h" #include "IpatchSF2VoiceCache.h" #include "IpatchSF2Zone.h" #include "IpatchSF2GenItem.h" /** * _ipatch_sf2_voice_cache_init_SF2: (skip) */ void _ipatch_sf2_voice_cache_init_SF2(void) { g_type_class_ref(IPATCH_TYPE_CONVERTER_SF2_PRESET_TO_SF2_VOICE_CACHE); g_type_class_ref(IPATCH_TYPE_CONVERTER_SF2_PZONE_TO_SF2_VOICE_CACHE); g_type_class_ref(IPATCH_TYPE_CONVERTER_SF2_INST_TO_SF2_VOICE_CACHE); g_type_class_ref(IPATCH_TYPE_CONVERTER_SF2_IZONE_TO_SF2_VOICE_CACHE); g_type_class_ref(IPATCH_TYPE_CONVERTER_SF2_SAMPLE_TO_SF2_VOICE_CACHE); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_SF2_PRESET_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_SF2_PRESET, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_SF2_PZONE_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_SF2_PZONE, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_SF2_INST_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_SF2_INST, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_SF2_IZONE_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_SF2_IZONE, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_SF2_SAMPLE_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_SF2_SAMPLE, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); } static gboolean _sf2_preset_to_sf2_voice_cache_convert(IpatchConverter *converter, GError **err) { IpatchSF2Preset *preset; IpatchSF2Inst *inst; IpatchSF2VoiceCache *cache; IpatchSF2Voice *voice; IpatchSF2GenArray *gpz; IpatchSF2GenArray pz; IpatchSF2GenArray *giz; IpatchSF2Zone *pzone, *izone; IpatchSF2Sample *sample; IpatchSF2GenAmount *amt; IpatchItem *solo_item; GObject *obj; GSList *gpmods = NULL, *pmods; /* global and preset modulator lists */ GSList *gimods; /* global and instrument modulator lists */ GSList *imods; /* local instrument modulators */ const GSList *defmods; /* default modulator list */ GSList *tmpmods; GSList *p, *p2; obj = IPATCH_CONVERTER_INPUT(converter); cache = IPATCH_SF2_VOICE_CACHE(IPATCH_CONVERTER_OUTPUT(converter)); solo_item = ((IpatchConverterSF2VoiceCache *)converter)->solo_item; /* check if its a preset zone or a preset */ if(IPATCH_IS_SF2_PZONE(obj)) /* ++ ref parent preset */ { preset = IPATCH_SF2_PRESET(ipatch_item_get_parent(IPATCH_ITEM(obj))); } else { preset = IPATCH_SF2_PRESET(obj); } /* declare the preset as a dependent item */ ipatch_sf2_voice_cache_declare_item(cache, (GObject *)preset); defmods = cache->default_mods; /* get default mod list */ IPATCH_ITEM_RLOCK(preset); /* ++ LOCK preset */ /* global generators and modulators (can access as long as preset is locked) */ gpz = &preset->genarray; gpmods = preset->mods; for(p = preset->zones; p; p = p->next) /* loop over preset zones */ { pzone = (IpatchSF2Zone *)(p->data); /* If a zone is currently solo, skip other zones */ if(solo_item && (IpatchItem *)pzone != solo_item) { continue; } ipatch_sf2_voice_cache_declare_item(cache, (GObject *)pzone); /* copy def/global gen values */ memcpy(&pz, gpz, sizeof(IpatchSF2GenArray)); IPATCH_ITEM_RLOCK(pzone); /* ++ LOCK preset zone */ inst = (IpatchSF2Inst *)(pzone->item); if(!inst) /* skip any zones without instrument ref */ { IPATCH_ITEM_RUNLOCK(pzone); continue; } ipatch_sf2_gen_item_copy_set((IpatchSF2GenItem *)pzone, &pz); /* process pzone */ if(pzone->mods) { pmods = ipatch_sf2_mod_list_override(gpmods, pzone->mods, FALSE); } else { pmods = gpmods; } IPATCH_ITEM_RLOCK(inst); /* ++ LOCK inst */ /* process global generators and modulators */ giz = &inst->genarray; if(inst->mods) /* can use modulators while inst is locked (no copy) */ { gimods = ipatch_sf2_mod_list_override(defmods, inst->mods, FALSE); /* ++ alloc new mod list */ } else { gimods = (GSList *)defmods; } p2 = inst->zones; for(; p2; p2 = p2->next) /* loop over instrument zones */ { izone = (IpatchSF2Zone *)(p2->data); ipatch_sf2_voice_cache_declare_item(cache, (GObject *)izone); IPATCH_ITEM_RLOCK(izone); /* ++ LOCK izone */ sample = (IpatchSF2Sample *)(izone->item); /* skip zones without sample or which don't note/velocity intersect */ if(!sample || !ipatch_sf2_gen_item_intersect_test((IpatchSF2GenItem *)izone, &pz)) { IPATCH_ITEM_RUNLOCK(izone); continue; } voice = ipatch_sf2_voice_cache_add_voice(cache); /* copy global inst gen values */ memcpy(&voice->gen_array, giz, sizeof(IpatchSF2GenArray)); /* zone overrides */ ipatch_sf2_gen_item_copy_set((IpatchSF2GenItem *)izone, &voice->gen_array); if(izone->mods) /* ++ override global/def mods with zone (no copy) */ { imods = ipatch_sf2_mod_list_override(gimods, izone->mods, FALSE); } else { imods = gimods; } /* igens += pgens */ ipatch_sf2_gen_array_offset(&voice->gen_array, &pz); /* ++ new modulator list of imods + pmods */ voice->mod_list = ipatch_sf2_mod_list_offset(imods, pmods); if(cache->override_mods) { tmpmods = voice->mod_list; voice->mod_list = ipatch_sf2_mod_list_override(tmpmods, cache->override_mods, TRUE); if(tmpmods) { ipatch_sf2_mod_list_free(tmpmods, TRUE); } } /* set MIDI note and velocity ranges */ amt = &voice->gen_array.values[IPATCH_SF2_GEN_NOTE_RANGE]; ipatch_sf2_voice_cache_set_voice_range(cache, voice, 0, amt->range.low, amt->range.high); amt = &voice->gen_array.values[IPATCH_SF2_GEN_VELOCITY_RANGE]; ipatch_sf2_voice_cache_set_voice_range(cache, voice, 1, amt->range.low, amt->range.high); /* copy sample parameters */ ipatch_sf2_voice_cache_declare_item(cache, (GObject *)sample); ipatch_sf2_voice_set_sample_data(voice, sample->sample_data); voice->loop_start = sample->loop_start; voice->loop_end = sample->loop_end; voice->rate = sample->rate; voice->root_note = sample->root_note; voice->fine_tune = sample->fine_tune; IPATCH_ITEM_RUNLOCK(izone); /* -- UNLOCK izone */ /* free imods if not == gimods (only list, since mod data not copied) */ if(imods != gimods) { ipatch_sf2_mod_list_free(imods, FALSE); } } IPATCH_ITEM_RUNLOCK(inst); /* -- UNLOCK inst */ IPATCH_ITEM_RUNLOCK(pzone); /* -- UNLOCK preset zone */ /* free gimods if not defmods (mod data was not copied) */ if(gimods != defmods) { ipatch_sf2_mod_list_free(gimods, FALSE); } /* free pmods list if not gpmods (mod data was not copied) */ if(pmods != gpmods) { ipatch_sf2_mod_list_free(pmods, FALSE); } } IPATCH_ITEM_RUNLOCK(preset); /* -- UNLOCK preset */ /* if convert object was preset zone, unref parent preset */ if((void *)obj != (void *)preset) { g_object_unref(preset); /* -- unref parent preset */ } return (TRUE); } /* use preset to voice cache converter function */ #define _sf2_pzone_to_sf2_voice_cache_convert \ _sf2_preset_to_sf2_voice_cache_convert static gboolean _sf2_inst_to_sf2_voice_cache_convert(IpatchConverter *converter, GError **err) { IpatchSF2Inst *inst; IpatchSF2VoiceCache *cache; IpatchSF2Voice *voice; IpatchSF2GenArray *giz; IpatchSF2Zone *izone; IpatchSF2Sample *sample; IpatchSF2GenAmount *amt; GObject *obj; GSList *gimods = NULL; const GSList *defmods; GSList *tmpmods; IpatchItem *solo_item; GSList *p; obj = IPATCH_CONVERTER_INPUT(converter); cache = IPATCH_SF2_VOICE_CACHE(IPATCH_CONVERTER_OUTPUT(converter)); solo_item = ((IpatchConverterSF2VoiceCache *)converter)->solo_item; /* check if its a instrument zone or a instrument */ if(IPATCH_IS_SF2_IZONE(obj)) /* ++ ref parent instrument */ { inst = IPATCH_SF2_INST(ipatch_item_get_parent(IPATCH_ITEM(obj))); } else { inst = IPATCH_SF2_INST(obj); } /* declare the instrument as a dependent item */ ipatch_sf2_voice_cache_declare_item(cache, (GObject *)inst); defmods = cache->default_mods; IPATCH_ITEM_RLOCK(inst); /* ++ LOCK instrument */ /* global generators and modulators (can access as long as inst is locked) */ giz = &inst->genarray; if(inst->mods) /* don't need to copy mods, under LOCK */ { gimods = ipatch_sf2_mod_list_override(defmods, inst->mods, FALSE); } else { gimods = (GSList *)defmods; } p = inst->zones; for(; p; p = p->next) { izone = (IpatchSF2Zone *)(p->data); /* If a zone is currently solo, skip other zones */ if(solo_item && (IpatchItem *)izone != solo_item) { continue; } ipatch_sf2_voice_cache_declare_item(cache, (GObject *)izone); IPATCH_ITEM_RLOCK(izone); /* ++ LOCK izone */ sample = (IpatchSF2Sample *)(izone->item); if(!sample) /* skip zones without sample ref */ { IPATCH_ITEM_RUNLOCK(izone); /* -- unlock izone */ continue; } voice = ipatch_sf2_voice_cache_add_voice(cache); /* copy global gen values */ memcpy(&voice->gen_array, giz, sizeof(IpatchSF2GenArray)); /* zone overrides global */ ipatch_sf2_gen_item_copy_set((IpatchSF2GenItem *)izone, &voice->gen_array); if(cache->override_mods) { /* Override global/default mods with zone (no copy) and then with cache override mods (copy) */ tmpmods = ipatch_sf2_mod_list_override(gimods, izone->mods, FALSE); voice->mod_list = ipatch_sf2_mod_list_override(tmpmods, cache->override_mods, TRUE); ipatch_sf2_mod_list_free(tmpmods, FALSE); } /* override global/default mods with zone (copy) */ else { voice->mod_list = ipatch_sf2_mod_list_override(gimods, izone->mods, TRUE); } /* set MIDI note and velocity ranges */ amt = &voice->gen_array.values[IPATCH_SF2_GEN_NOTE_RANGE]; ipatch_sf2_voice_cache_set_voice_range(cache, voice, 0, amt->range.low, amt->range.high); amt = &voice->gen_array.values[IPATCH_SF2_GEN_VELOCITY_RANGE]; ipatch_sf2_voice_cache_set_voice_range(cache, voice, 1, amt->range.low, amt->range.high); ipatch_sf2_voice_cache_declare_item(cache, (GObject *)sample); ipatch_sf2_voice_set_sample_data(voice, sample->sample_data); voice->loop_start = sample->loop_start; voice->loop_end = sample->loop_end; voice->rate = sample->rate; voice->root_note = sample->root_note; voice->fine_tune = sample->fine_tune; IPATCH_ITEM_RUNLOCK(izone); /* -- UNLOCK izone */ } IPATCH_ITEM_RUNLOCK(inst); /* -- UNLOCK instrument */ /* free gimods (mod data was not copied) */ if(gimods != defmods) { ipatch_sf2_mod_list_free(gimods, FALSE); } /* if convert object was instrument zone, unref parent instrument */ if((void *)obj != (void *)inst) { g_object_unref(inst); /* -- unref parent instrument */ } return (TRUE); } /* use instrument to voice cache converter function */ #define _sf2_izone_to_sf2_voice_cache_convert \ _sf2_inst_to_sf2_voice_cache_convert static gboolean _sf2_sample_to_sf2_voice_cache_convert(IpatchConverter *converter, GError **err) { IpatchSF2Sample *sample; IpatchSF2VoiceCache *cache; IpatchSF2Voice *voice; IpatchSF2GenAmount *amt; sample = IPATCH_SF2_SAMPLE(IPATCH_CONVERTER_INPUT(converter)); cache = IPATCH_SF2_VOICE_CACHE(IPATCH_CONVERTER_OUTPUT(converter)); ipatch_sf2_voice_cache_declare_item(cache, (GObject *)sample); voice = ipatch_sf2_voice_cache_add_voice(cache); voice->mod_list = ipatch_sf2_mod_list_override(cache->default_mods, cache->override_mods, TRUE); /* set MIDI note and velocity ranges */ amt = &voice->gen_array.values[IPATCH_SF2_GEN_NOTE_RANGE]; ipatch_sf2_voice_cache_set_voice_range(cache, voice, 0, amt->range.low, amt->range.high); amt = &voice->gen_array.values[IPATCH_SF2_GEN_VELOCITY_RANGE]; ipatch_sf2_voice_cache_set_voice_range(cache, voice, 1, amt->range.low, amt->range.high); /* use the default loop type for the IpatchSF2VoiceCache */ voice->gen_array.values[IPATCH_SF2_GEN_SAMPLE_MODES].sword = cache->default_loop_type; IPATCH_SF2_GEN_ARRAY_SET_FLAG(&voice->gen_array, IPATCH_SF2_GEN_SAMPLE_MODES); ipatch_sf2_voice_set_sample_data(voice, sample->sample_data); voice->loop_start = sample->loop_start; voice->loop_end = sample->loop_end; voice->rate = sample->rate; voice->root_note = sample->root_note; voice->fine_tune = sample->fine_tune; return (TRUE); } CONVERTER_CLASS_INIT(sf2_preset_to_sf2_voice_cache) CONVERTER_CLASS_INIT(sf2_pzone_to_sf2_voice_cache) CONVERTER_CLASS_INIT(sf2_inst_to_sf2_voice_cache) CONVERTER_CLASS_INIT(sf2_izone_to_sf2_voice_cache) CONVERTER_CLASS_INIT(sf2_sample_to_sf2_voice_cache) CONVERTER_SF2_VOICE_CACHE_GET_TYPE(sf2_preset_to_sf2_voice_cache, SF2PresetToSF2VoiceCache) CONVERTER_SF2_VOICE_CACHE_GET_TYPE(sf2_pzone_to_sf2_voice_cache, SF2PZoneToSF2VoiceCache) CONVERTER_SF2_VOICE_CACHE_GET_TYPE(sf2_inst_to_sf2_voice_cache, SF2InstToSF2VoiceCache) CONVERTER_SF2_VOICE_CACHE_GET_TYPE(sf2_izone_to_sf2_voice_cache, SF2IZoneToSF2VoiceCache) CONVERTER_SF2_VOICE_CACHE_GET_TYPE(sf2_sample_to_sf2_voice_cache, SF2SampleToSF2VoiceCache) libinstpatch-1.1.6/libinstpatch/IpatchSF2VoiceCache_SF2.h000066400000000000000000000055121400263525300231520ustar00rootroot00000000000000/* * IpatchSF2VoiceCache_SF2.h - Voice cache converters for SoundFont types * * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. * */ #ifndef __IPATCH_SF2_VOICE_CACHE_SF2_H__ #define __IPATCH_SF2_VOICE_CACHE_SF2_H__ #include #include #include typedef IpatchConverterSF2VoiceCache IpatchConverterSF2PresetToSF2VoiceCache; typedef IpatchConverterSF2VoiceCacheClass IpatchConverterSF2PresetToSF2VoiceCacheClass; typedef IpatchConverterSF2VoiceCache IpatchConverterSF2PZoneToSF2VoiceCache; typedef IpatchConverterSF2VoiceCacheClass IpatchConverterSF2PZoneToSF2VoiceCacheClass; typedef IpatchConverterSF2VoiceCache IpatchConverterSF2InstToSF2VoiceCache; typedef IpatchConverterSF2VoiceCacheClass IpatchConverterSF2InstToSF2VoiceCacheClass; typedef IpatchConverterSF2VoiceCache IpatchConverterSF2IZoneToSF2VoiceCache; typedef IpatchConverterSF2VoiceCacheClass IpatchConverterSF2IZoneToSF2VoiceCacheClass; typedef IpatchConverterSF2VoiceCache IpatchConverterSF2SampleToSF2VoiceCache; typedef IpatchConverterSF2VoiceCacheClass IpatchConverterSF2SampleToSF2VoiceCacheClass; #define IPATCH_TYPE_CONVERTER_SF2_PRESET_TO_SF2_VOICE_CACHE \ (ipatch_converter_sf2_preset_to_sf2_voice_cache_get_type ()) #define IPATCH_TYPE_CONVERTER_SF2_PZONE_TO_SF2_VOICE_CACHE \ (ipatch_converter_sf2_pzone_to_sf2_voice_cache_get_type ()) #define IPATCH_TYPE_CONVERTER_SF2_INST_TO_SF2_VOICE_CACHE \ (ipatch_converter_sf2_inst_to_sf2_voice_cache_get_type ()) #define IPATCH_TYPE_CONVERTER_SF2_IZONE_TO_SF2_VOICE_CACHE \ (ipatch_converter_sf2_izone_to_sf2_voice_cache_get_type ()) #define IPATCH_TYPE_CONVERTER_SF2_SAMPLE_TO_SF2_VOICE_CACHE \ (ipatch_converter_sf2_sample_to_sf2_voice_cache_get_type ()) GType ipatch_converter_sf2_preset_to_sf2_voice_cache_get_type(void); GType ipatch_converter_sf2_pzone_to_sf2_voice_cache_get_type(void); GType ipatch_converter_sf2_inst_to_sf2_voice_cache_get_type(void); GType ipatch_converter_sf2_izone_to_sf2_voice_cache_get_type(void); GType ipatch_converter_sf2_sample_to_sf2_voice_cache_get_type(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2VoiceCache_SLI.c000066400000000000000000000157741400263525300232150ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #include #include #include #include #include "IpatchSF2VoiceCache_SLI.h" #include "IpatchConverter.h" #include "IpatchConverterSF2VoiceCache.h" #include "IpatchConverter_priv.h" #include "IpatchSLIInst.h" #include "IpatchSLIZone.h" #include "IpatchSLISample.h" #include "IpatchSF2VoiceCache.h" #include "IpatchSample.h" /** * _ipatch_sf2_voice_cache_init_SLI: (skip) */ void _ipatch_sf2_voice_cache_init_SLI(void) { g_type_class_ref(IPATCH_TYPE_CONVERTER_SLI_INST_TO_SF2_VOICE_CACHE); g_type_class_ref(IPATCH_TYPE_CONVERTER_SLI_ZONE_TO_SF2_VOICE_CACHE); g_type_class_ref(IPATCH_TYPE_CONVERTER_SLI_SAMPLE_TO_SF2_VOICE_CACHE); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_SLI_INST_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_SLI_INST, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_SLI_ZONE_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_SLI_ZONE, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_SLI_SAMPLE_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_SLI_SAMPLE, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); } static gboolean _sli_inst_to_sf2_voice_cache_convert(IpatchConverter *converter, GError **err) { IpatchSLIInst *inst; IpatchSF2VoiceCache *cache; IpatchSF2Voice *voice; IpatchSLIZone *zone; IpatchSLISample *sample; IpatchSF2GenAmount *amt; IpatchItem *solo_item; GObject *obj; GSList *p; obj = IPATCH_CONVERTER_INPUT(converter); cache = IPATCH_SF2_VOICE_CACHE(IPATCH_CONVERTER_OUTPUT(converter)); solo_item = ((IpatchConverterSF2VoiceCache *)converter)->solo_item; /* check if it is an instrument or a zone */ if(IPATCH_IS_SLI_ZONE(obj)) /* ++ ref parent instrument */ { inst = IPATCH_SLI_INST(ipatch_item_get_parent(IPATCH_ITEM(obj))); } else { inst = IPATCH_SLI_INST(obj); } ipatch_sf2_voice_cache_declare_item(cache, (GObject *)inst); IPATCH_ITEM_RLOCK(inst); /* ++ LOCK instrument */ for(p = inst->zones; p; p = p->next) /* loop over zones */ { zone = (IpatchSLIZone *)(p->data); /* If a zone is currently solo, skip other zones */ if(solo_item && (IpatchItem *)zone != solo_item) { continue; } ipatch_sf2_voice_cache_declare_item(cache, (GObject *)zone); IPATCH_ITEM_RLOCK(zone); /* ++ LOCK zone */ sample = (IpatchSLISample *)(zone->sample); if(!sample) /* skip zones without sample ref */ { IPATCH_ITEM_RUNLOCK(zone); /* -- unlock izone */ continue; } voice = ipatch_sf2_voice_cache_add_voice(cache); voice->mod_list = ipatch_sf2_mod_list_duplicate(cache->default_mods); /* copy generator values */ memcpy(&voice->gen_array, &zone->genarray, sizeof(IpatchSF2GenArray)); /* set MIDI note and velocity ranges */ amt = &voice->gen_array.values[IPATCH_SF2_GEN_NOTE_RANGE]; ipatch_sf2_voice_cache_set_voice_range(cache, voice, 0, amt->range.low, amt->range.high); amt = &voice->gen_array.values[IPATCH_SF2_GEN_VELOCITY_RANGE]; ipatch_sf2_voice_cache_set_voice_range(cache, voice, 1, amt->range.low, amt->range.high); ipatch_sf2_voice_cache_declare_item(cache, (GObject *)sample); ipatch_sf2_voice_set_sample_data(voice, sample->sample_data); voice->rate = sample->rate; voice->loop_start = sample->loop_start; voice->loop_end = sample->loop_end; voice->root_note = sample->root_note; voice->fine_tune = sample->fine_tune; IPATCH_ITEM_RUNLOCK(zone); /* -- UNLOCK zone */ } IPATCH_ITEM_RUNLOCK(inst); /* -- UNLOCK instrument */ /* if convert object was a zone, unref parent instrument */ if((void *)obj != (void *)inst) { g_object_unref(inst); /* -- unref parent instrument */ } return (TRUE); } /* use the instrument converter for zone also */ #define _sli_zone_to_sf2_voice_cache_convert \ _sli_inst_to_sf2_voice_cache_convert static gboolean _sli_sample_to_sf2_voice_cache_convert(IpatchConverter *converter, GError **err) { IpatchSLISample *sample; IpatchSF2VoiceCache *cache; IpatchSF2Voice *voice; IpatchSF2GenAmount *amt; sample = IPATCH_SLI_SAMPLE(IPATCH_CONVERTER_INPUT(converter)); cache = IPATCH_SF2_VOICE_CACHE(IPATCH_CONVERTER_OUTPUT(converter)); ipatch_sf2_voice_cache_declare_item(cache, (GObject *)sample); voice = ipatch_sf2_voice_cache_add_voice(cache); voice->mod_list = ipatch_sf2_mod_list_duplicate(cache->default_mods); /* set MIDI note and velocity ranges */ amt = &voice->gen_array.values[IPATCH_SF2_GEN_NOTE_RANGE]; ipatch_sf2_voice_cache_set_voice_range(cache, voice, 0, amt->range.low, amt->range.high); amt = &voice->gen_array.values[IPATCH_SF2_GEN_VELOCITY_RANGE]; ipatch_sf2_voice_cache_set_voice_range(cache, voice, 1, amt->range.low, amt->range.high); /* use the default loop type for the IpatchSF2VoiceCache */ voice->gen_array.values[IPATCH_SF2_GEN_SAMPLE_MODES].sword = cache->default_loop_type; IPATCH_SF2_GEN_ARRAY_SET_FLAG(&voice->gen_array, IPATCH_SF2_GEN_SAMPLE_MODES); ipatch_sf2_voice_set_sample_data(voice, sample->sample_data); voice->rate = sample->rate; voice->loop_start = sample->loop_start; voice->loop_end = sample->loop_end; voice->root_note = sample->root_note; voice->fine_tune = sample->fine_tune; return (TRUE); } CONVERTER_CLASS_INIT(sli_inst_to_sf2_voice_cache) CONVERTER_CLASS_INIT(sli_zone_to_sf2_voice_cache) CONVERTER_CLASS_INIT(sli_sample_to_sf2_voice_cache) CONVERTER_SF2_VOICE_CACHE_GET_TYPE(sli_inst_to_sf2_voice_cache, SLIInstToSF2VoiceCache) CONVERTER_SF2_VOICE_CACHE_GET_TYPE(sli_zone_to_sf2_voice_cache, SLIZoneToSF2VoiceCache) CONVERTER_SF2_VOICE_CACHE_GET_TYPE(sli_sample_to_sf2_voice_cache, SLISampleToSF2VoiceCache) libinstpatch-1.1.6/libinstpatch/IpatchSF2VoiceCache_SLI.h000066400000000000000000000042321400263525300232050ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_VOICE_CACHE_SLI_H__ #define __IPATCH_SF2_VOICE_CACHE_SLI_H__ #include #include #include #include typedef IpatchConverterSF2VoiceCache IpatchConverterSLIInstToSF2VoiceCache; typedef IpatchConverterSF2VoiceCacheClass IpatchConverterSLIInstToSF2VoiceCacheClass; typedef IpatchConverterSF2VoiceCache IpatchConverterSLIZoneToSF2VoiceCache; typedef IpatchConverterSF2VoiceCacheClass IpatchConverterSLIZoneToSF2VoiceCacheClass; typedef IpatchConverterSF2VoiceCache IpatchConverterSLISampleToSF2VoiceCache; typedef IpatchConverterSF2VoiceCacheClass IpatchConverterSLISampleToSF2VoiceCacheClass; #define IPATCH_TYPE_CONVERTER_SLI_INST_TO_SF2_VOICE_CACHE \ (ipatch_converter_sli_inst_to_sf2_voice_cache_get_type ()) #define IPATCH_TYPE_CONVERTER_SLI_ZONE_TO_SF2_VOICE_CACHE \ (ipatch_converter_sli_zone_to_sf2_voice_cache_get_type ()) #define IPATCH_TYPE_CONVERTER_SLI_SAMPLE_TO_SF2_VOICE_CACHE \ (ipatch_converter_sli_sample_to_sf2_voice_cache_get_type ()) GType ipatch_converter_sli_inst_to_sf2_voice_cache_get_type(void); GType ipatch_converter_sli_zone_to_sf2_voice_cache_get_type(void); GType ipatch_converter_sli_sample_to_sf2_voice_cache_get_type(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2VoiceCache_VBank.c000066400000000000000000000210061400263525300235500ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2VoiceCache_VBank * @short_description: Voice cache converters for VBank types * @see_also: * @stability: Stable */ #include #include #include #include #include "IpatchSF2VoiceCache_VBank.h" #include "IpatchConverter.h" #include "IpatchConverter_priv.h" #include "IpatchSF2VoiceCache.h" #include "IpatchVBankInst.h" #include "IpatchVBankRegion.h" #include "misc.h" #include "i18n.h" /** * _ipatch_sf2_voice_cache_init_VBank: (skip) */ void _ipatch_sf2_voice_cache_init_VBank(void) { g_type_class_ref(IPATCH_TYPE_CONVERTER_VBANK_INST_TO_SF2_VOICE_CACHE); g_type_class_ref(IPATCH_TYPE_CONVERTER_VBANK_REGION_TO_SF2_VOICE_CACHE); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_VBANK_INST_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_VBANK_INST, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); ipatch_register_converter_map (IPATCH_TYPE_CONVERTER_VBANK_REGION_TO_SF2_VOICE_CACHE, 0, 0, IPATCH_TYPE_VBANK_REGION, 0, 1, IPATCH_TYPE_SF2_VOICE_CACHE, 0, 1); } static gboolean _vbank_inst_to_sf2_voice_cache_convert(IpatchConverter *converter, GError **err) { IpatchVBankInst *inst; IpatchSF2VoiceCache *cache, *item_vcache; IpatchVBankRegion *region; IpatchConverter *itemconv; IpatchSF2Voice *voice, *item_voice; GObject *obj; GSList *p; int note_index; int *note_range; int note_low, note_high; GError *localerr = NULL; guint voicendx; int root_note; obj = IPATCH_CONVERTER_INPUT(converter); cache = IPATCH_SF2_VOICE_CACHE(IPATCH_CONVERTER_OUTPUT(converter)); /* find which selection index is the MIDI note range */ for(note_index = 0; note_index < cache->sel_count; note_index++) if(cache->sel_info[note_index].type == IPATCH_SF2_VOICE_SEL_NOTE) { break; } if(note_index == cache->sel_count) { note_index = -1; } else { note_index *= 2; /* convert to integer pair offset in ranges array */ } /* check if its a vbank instrument or region */ if(IPATCH_IS_VBANK_REGION(obj)) /* ++ ref parent instrument */ { inst = IPATCH_VBANK_INST(ipatch_item_get_parent(IPATCH_ITEM(obj))); } else { inst = IPATCH_VBANK_INST(obj); } /* declare the instrument as a dependent item */ ipatch_sf2_voice_cache_declare_item(cache, (GObject *)inst); IPATCH_ITEM_RLOCK(inst); /* ++ lock instrument */ for(p = inst->regions; p; p = p->next) { region = (IpatchVBankRegion *)(p->data); IPATCH_ITEM_RLOCK(region); /* ++ lock region */ /* ++ ref new converter for region's item */ itemconv = ipatch_create_converter(G_OBJECT_TYPE(region->item), IPATCH_TYPE_SF2_VOICE_CACHE); /* skip any regions with un-synthesizable items - and log a warning */ if(!itemconv) { IPATCH_ITEM_RUNLOCK(region); /* -- unlock region */ ipatch_converter_log(converter, G_OBJECT(region), IPATCH_CONVERTER_LOG_WARN, _("No voice handler for region item")); continue; } /* ++ ref - create voice cache for region's referenced item */ item_vcache = ipatch_sf2_voice_cache_new(cache->sel_info, cache->sel_count); ipatch_converter_add_input(itemconv, (GObject *)(region->item)); ipatch_converter_add_output(itemconv, (GObject *)item_vcache); if(!ipatch_converter_convert(itemconv, &localerr)) { IPATCH_ITEM_RUNLOCK(region); /* -- unlock region */ ipatch_converter_log_printf(converter, G_OBJECT(region), IPATCH_CONVERTER_LOG_WARN, _("Failed to convert region item to voices: %s"), ipatch_gerror_message(localerr)); g_clear_error(&localerr); g_object_unref(itemconv); /* -- unref */ g_object_unref(item_vcache); /* -- unref */ continue; } g_object_unref(itemconv); /* -- unref item converter */ note_low = region->note_range.low; note_high = region->note_range.high; /* loop over voices in item's voice cache */ for(voicendx = 0; voicendx < item_vcache->voices->len; voicendx++) { item_voice = IPATCH_SF2_VOICE_CACHE_GET_VOICE(item_vcache, voicendx); if(note_index >= 0) note_range = &g_array_index(item_vcache->ranges, int, item_voice->range_index + note_index); else { note_range = NULL; } /* if intersect note range mode.. */ if(region->note_range_mode == IPATCH_VBANK_REGION_NOTE_RANGE_MODE_INTERSECT && note_range) { /* note ranges are exclusive? - skip voice */ if(note_range[0] != IPATCH_SF2_VOICE_SEL_WILDCARD && note_range[1] != IPATCH_SF2_VOICE_SEL_WILDCARD && ((note_low < note_range[0] && note_high < note_range[0]) || (note_low > note_range[1] && note_high > note_range[1]))) { continue; } } voice = ipatch_sf2_voice_cache_add_voice(cache); ipatch_sf2_voice_copy(voice, item_voice); /* copy voice selection criteria */ memcpy(&g_array_index(cache->ranges, int, voice->range_index), &g_array_index(item_vcache->ranges, int, item_voice->range_index), 2 * cache->sel_count * sizeof(int)); /* modify note range depending on mode */ if(note_index >= 0) { note_range = &g_array_index(cache->ranges, int, voice->range_index + note_index); if(region->note_range_mode == IPATCH_VBANK_REGION_NOTE_RANGE_MODE_INTERSECT) { note_range[0] = MAX(note_low, note_range[0]); note_range[1] = MIN(note_high, note_range[1]); } else /* IPATCH_VBANK_REGION_NOTE_RANGE_MODE_OVERRIDE */ { note_range[0] = note_low; note_range[1] = note_high; } } /* modify root note tuning depending on mode */ if(region->root_note_mode == IPATCH_VBANK_REGION_ROOT_NOTE_MODE_OFFSET) { root_note = voice->root_note + region->root_note; voice->root_note = CLAMP(root_note, 0, 127); } /* IPATCH_VBANK_REGION_ROOT_NOTE_MODE_OVERRIDE */ else { voice->root_note = region->root_note; } } /* for voicendx in item_vcache->voices */ IPATCH_ITEM_RUNLOCK(region); /* -- unlock region */ g_object_unref(item_vcache); /* -- unref item voice cache */ } /* for p in regions */ IPATCH_ITEM_RUNLOCK(inst); /* -- unlock instrument */ /* if convert object was vbank region, unref parent instrument */ if((void *)obj != (void *)inst) { g_object_unref(inst); /* -- unref parent instrument */ } return (TRUE); } #define _vbank_region_to_sf2_voice_cache_convert \ _vbank_inst_to_sf2_voice_cache_convert CONVERTER_CLASS_INIT(vbank_inst_to_sf2_voice_cache) CONVERTER_CLASS_INIT(vbank_region_to_sf2_voice_cache) CONVERTER_GET_TYPE(vbank_inst_to_sf2_voice_cache, VBankInstToSF2VoiceCache) CONVERTER_GET_TYPE(vbank_region_to_sf2_voice_cache, VBankRegionToSF2VoiceCache) libinstpatch-1.1.6/libinstpatch/IpatchSF2VoiceCache_VBank.h000066400000000000000000000032431400263525300235600ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_VOICE_CACHE_VBANK_H__ #define __IPATCH_SF2_VOICE_CACHE_VBANK_H__ #include #include #include typedef IpatchConverter IpatchConverterVBankInstToSF2VoiceCache; typedef IpatchConverterClass IpatchConverterVBankInstToSF2VoiceCacheClass; typedef IpatchConverter IpatchConverterVBankRegionToSF2VoiceCache; typedef IpatchConverterClass IpatchConverterVBankRegionToSF2VoiceCacheClass; #define IPATCH_TYPE_CONVERTER_VBANK_INST_TO_SF2_VOICE_CACHE \ (ipatch_converter_vbank_inst_to_sf2_voice_cache_get_type ()) #define IPATCH_TYPE_CONVERTER_VBANK_REGION_TO_SF2_VOICE_CACHE \ (ipatch_converter_vbank_region_to_sf2_voice_cache_get_type ()) GType ipatch_converter_vbank_inst_to_sf2_voice_cache_get_type(void); GType ipatch_converter_vbank_region_to_sf2_voice_cache_get_type(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2Writer.c000066400000000000000000001673441400263525300217720ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2Writer * @short_description: SoundFont writer object * @see_also: * @stability: Stable * * Object for writing a tree of SoundFont objects (#IpatchSF2) to a SoundFont * file. */ #include #include #include #include "IpatchSF2Writer.h" #include "IpatchSF2File_priv.h" #include "IpatchBase.h" #include "IpatchFile.h" #include "IpatchSampleStore.h" #include "IpatchSampleStoreFile.h" #include "IpatchSampleStoreRom.h" #include "IpatchSampleStoreSwap.h" #include "IpatchSampleStoreSplit24.h" #include "IpatchSF2Preset.h" #include "IpatchSF2Inst.h" #include "IpatchSF2Sample.h" #include "IpatchSF2PZone.h" #include "IpatchSF2IZone.h" #include "IpatchSF2Zone.h" #include "IpatchUnit.h" #include "i18n.h" #include "ipatch_priv.h" #include "misc.h" #include "version.h" /* NOTICE: A duplicate SoundFont object is used for saving. This * solves all multi-thread issues and allows one to continue editing * even while a SoundFont is being saved. It also means that the * duplicate SoundFont object can be accessed directly without * locking. Sample data objects are not duplicated though, so they * still need to be dealt with properly. */ enum { PROP_0, PROP_MIGRATE_SAMPLES }; /* Hash value used for sample_hash */ typedef struct { guint index; /* Sample index */ guint position; /* Position in file */ guint position24; /* 24 bit byte file position (or 0 if 16 bit sample) */ } SampleHashValue; /* SoundFont 16 bit sample format */ #define FORMAT_16BIT IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_MONO \ | IPATCH_SAMPLE_SIGNED | IPATCH_SAMPLE_LENDIAN /* SoundFont 24 bit sample format */ #define FORMAT_24BIT IPATCH_SAMPLE_24BIT | IPATCH_SAMPLE_MONO \ | IPATCH_SAMPLE_SIGNED | IPATCH_SAMPLE_LENDIAN static void ipatch_sf2_writer_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sf2_writer_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static SampleHashValue *ipatch_sf2_writer_sample_hash_value_new(void); static void ipatch_sf2_writer_sample_hash_value_destroy(gpointer value); static void ipatch_sf2_writer_finalize(GObject *object); static gboolean ipatch_sf2_write_level_0(IpatchSF2Writer *writer, GError **err); static gboolean sfont_write_info(IpatchSF2Writer *writer, GError **err); static gboolean sfont_write_strchunk(IpatchSF2Writer *writer, guint32 id, const char *val, GError **err); static gboolean sfont_write_samples(IpatchSF2Writer *writer, GError **err); static gboolean sfont_write_samples24(IpatchSF2Writer *writer, GError **err); static gboolean sfont_write_phdrs(IpatchSF2Writer *writer, GError **err); static gboolean sfont_write_pbags(IpatchSF2Writer *writer, GError **err); static gboolean sfont_write_pmods(IpatchSF2Writer *writer, GError **err); static gboolean sfont_write_pgens(IpatchSF2Writer *writer, GError **err); static gboolean sfont_write_ihdrs(IpatchSF2Writer *writer, GError **err); static gboolean sfont_write_ibags(IpatchSF2Writer *writer, GError **err); static gboolean sfont_write_imods(IpatchSF2Writer *writer, GError **err); static gboolean sfont_write_igens(IpatchSF2Writer *writer, GError **err); static gboolean sfont_write_shdrs(IpatchSF2Writer *writer, GError **err); G_DEFINE_TYPE(IpatchSF2Writer, ipatch_sf2_writer, IPATCH_TYPE_RIFF) static void ipatch_sf2_writer_class_init(IpatchSF2WriterClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->set_property = ipatch_sf2_writer_set_property; obj_class->get_property = ipatch_sf2_writer_get_property; obj_class->finalize = ipatch_sf2_writer_finalize; /** * IpatchSF2Writer:migrate-samples: * * Was supposed to migrate sample data to the new file, was not implemented properly though. * Does nothing now. * * Deprecated: 1.1.0: Use ipatch_sf2_writer_create_stores() instead. **/ g_object_class_install_property(obj_class, PROP_MIGRATE_SAMPLES, g_param_spec_boolean("migrate-samples", _("Migrate Samples"), _("Migrate samples to new file"), FALSE, G_PARAM_READWRITE)); } static void ipatch_sf2_writer_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { switch(property_id) { case PROP_MIGRATE_SAMPLES: break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_sf2_writer_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { switch(property_id) { case PROP_MIGRATE_SAMPLES: break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_sf2_writer_init(IpatchSF2Writer *writer) { writer->inst_hash = g_hash_table_new(NULL, NULL); writer->sample_hash = g_hash_table_new_full(NULL, NULL, NULL, ipatch_sf2_writer_sample_hash_value_destroy); } static SampleHashValue * ipatch_sf2_writer_sample_hash_value_new(void) { return (g_slice_new0(SampleHashValue)); } static void ipatch_sf2_writer_sample_hash_value_destroy(gpointer value) { g_slice_free(SampleHashValue, value); } static void ipatch_sf2_writer_finalize(GObject *object) { IpatchSF2Writer *writer = IPATCH_SF2_WRITER(object); if(writer->orig_sf) { g_object_unref(writer->orig_sf); } if(writer->sf) { g_object_unref(writer->sf); } g_hash_table_destroy(writer->inst_hash); g_hash_table_destroy(writer->sample_hash); if(writer->store_list) { g_object_unref(writer->store_list); } if(G_OBJECT_CLASS(ipatch_sf2_writer_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_sf2_writer_parent_class)->finalize(object); } } /** * ipatch_sf2_writer_new: * @handle: SoundFont file handle to save to or %NULL to set later * @sfont: SoundFont object to save or %NULL to set later * * Create a new SoundFont 2 file writer. * * Returns: The new SoundFont writer */ IpatchSF2Writer * ipatch_sf2_writer_new(IpatchFileHandle *handle, IpatchSF2 *sfont) { IpatchSF2Writer *writer; g_return_val_if_fail(!sfont || IPATCH_IS_SF2(sfont), NULL); g_return_val_if_fail(!handle || IPATCH_IS_SF2_FILE(handle->file), NULL); writer = g_object_new(IPATCH_TYPE_SF2_WRITER, NULL); if(sfont) { ipatch_sf2_writer_set_patch(writer, sfont); } if(handle) { ipatch_sf2_writer_set_file_handle(writer, handle); } return (writer); } /** * ipatch_sf2_writer_set_patch: * @writer: SoundFont writer object * @sfont: SoundFont patch to save * * Set the SoundFont patch object to save with a SoundFont writer. */ void ipatch_sf2_writer_set_patch(IpatchSF2Writer *writer, IpatchSF2 *sfont) { g_return_if_fail(IPATCH_IS_SF2_WRITER(writer)); g_return_if_fail(IPATCH_IS_SF2(sfont)); if(writer->orig_sf) { g_object_unref(writer->orig_sf); } writer->orig_sf = g_object_ref(sfont); } /** * ipatch_sf2_writer_set_file: * @writer: SoundFont writer object * @handle: SoundFont file handle * * Set the SoundFont file handle of a SoundFont writer. A convenience * function, since ipatch_riff_set_file_handle() could also be used, albeit * without stricter type casting. */ void ipatch_sf2_writer_set_file_handle(IpatchSF2Writer *writer, IpatchFileHandle *handle) { g_return_if_fail(IPATCH_IS_SF2_WRITER(writer)); g_return_if_fail(handle && IPATCH_IS_SF2_FILE(handle->file)); ipatch_riff_set_file_handle(IPATCH_RIFF(writer), handle); } /** * ipatch_sf2_writer_save: * @writer: SoundFont writer object * @err: Location to store error info or %NULL * * Write a SoundFont object to a file. * * Returns: %TRUE on success, %FALSE on error */ gboolean ipatch_sf2_writer_save(IpatchSF2Writer *writer, GError **err) { IpatchRiff *riff; gboolean b; g_return_val_if_fail(IPATCH_IS_SF2_WRITER(writer), FALSE); g_return_val_if_fail(!err || !*err, FALSE); g_return_val_if_fail(writer->orig_sf != NULL, FALSE); /* shouldn't be set, but.. */ if(writer->sf) { g_object_unref(writer->sf); } /* set SoundFont version according to whether 24 bit samples are enabled */ b = (ipatch_item_get_flags(IPATCH_ITEM(writer->orig_sf)) & IPATCH_SF2_SAMPLES_24BIT) != 0; g_object_set(writer->orig_sf, "version", b ? "2.04" : "2.01", NULL); /* duplicate for save, so we can be multi-thread friendly :) ++ ref new duplicate object */ writer->sf = IPATCH_SF2(ipatch_item_duplicate(IPATCH_ITEM(writer->orig_sf))); riff = IPATCH_RIFF(writer); /* write toplevel SoundFont RIFF chunk */ if(!ipatch_riff_write_chunk(riff, IPATCH_RIFF_CHUNK_RIFF, IPATCH_SFONT_FOURCC_SFBK, err)) { return (FALSE); } if(!ipatch_sf2_write_level_0(writer, err)) { goto err; } /* close the RIFF chunk */ if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } g_object_set(writer->orig_sf, "changed", FALSE, /* file and object are in sync */ "saved", TRUE, /* has now been saved */ NULL); return (TRUE); err: g_object_unref(writer->sf); /* -- unref duplicate SoundFont */ writer->sf = NULL; return (FALSE); } /** * ipatch_sf2_writer_create_stores: * @writer: SoundFont writer object * * Create sample stores and add them to applicable #IpatchSampleData objects and return object list. * This function can be called multiple times, additional calls will return the same list. * * Returns: (transfer full): List of sample stores which the caller owns a reference to or %NULL * * Since: 1.1.0 */ IpatchList * ipatch_sf2_writer_create_stores(IpatchSF2Writer *writer) { SampleHashValue *hash_value; IpatchSample *newstore; IpatchFile *save_file; IpatchSF2Sample *sample; IpatchIter iter; gboolean smpl24; IpatchList *list; int rate, format; guint size; g_return_val_if_fail(writer->sf != NULL, NULL); // Return existing store list (if this function has been called before) if(writer->store_list) { return (g_object_ref(writer->store_list)); // ++ ref for caller } save_file = IPATCH_RIFF(writer)->handle->file; smpl24 = (ipatch_item_get_flags(writer->sf) & IPATCH_SF2_SAMPLES_24BIT) != 0; if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sf), &iter, IPATCH_TYPE_SF2_SAMPLE)) { return (NULL); } list = ipatch_list_new(); // ++ ref list /* traverse samples */ for(sample = ipatch_sf2_sample_first(&iter); sample; sample = ipatch_sf2_sample_next(&iter)) { hash_value = g_hash_table_lookup(writer->sample_hash, sample); /* Skip ROM samples, hash_value should never be NULL, but.. */ if(!hash_value || hash_value->position == 0) { continue; } g_object_get(sample, "sample-format", &format, "sample-size", &size, "sample-rate", &rate, NULL); // Create 16 bit sample store if SoundFont does not contain 24 bit or original sample was 16 bit or less if(!smpl24 || IPATCH_SAMPLE_FORMAT_GET_WIDTH(format) <= IPATCH_SAMPLE_16BIT) { /* ++ ref newstore */ newstore = ipatch_sample_store_file_new(save_file, hash_value->position); format = FORMAT_16BIT; } else /* ++ ref newstore */ { newstore = ipatch_sample_store_split24_new(save_file, hash_value->position, hash_value->position24); format = FORMAT_24BIT; } g_object_set(newstore, "sample-format", format, "sample-size", size, "sample-rate", rate, NULL); ipatch_sample_data_add(sample->sample_data, (IpatchSampleStore *)newstore); list->items = g_list_prepend(list->items, newstore); // !! list takes over reference } writer->store_list = g_object_ref(list); // ++ ref for writer object return (list); // !! caller takes over reference } /** * ipatch_sf2_write_phdr: (skip) * @handle: File handle to buffer writes to, commit after calling this function * @phdr: Preset header structure to store * * Buffer writes a preset header into @handle from a @phdr structure. */ void ipatch_sf2_write_phdr(IpatchFileHandle *handle, const IpatchSF2Phdr *phdr) { g_return_if_fail(handle != NULL); g_return_if_fail(phdr != NULL); ipatch_file_buf_write(handle, phdr->name, IPATCH_SFONT_NAME_SIZE); ipatch_file_buf_write_u16(handle, phdr->program); ipatch_file_buf_write_u16(handle, phdr->bank); ipatch_file_buf_write_u16(handle, phdr->bag_index); ipatch_file_buf_write_u32(handle, phdr->library); ipatch_file_buf_write_u32(handle, phdr->genre); ipatch_file_buf_write_u32(handle, phdr->morphology); } /** * ipatch_sf2_write_ihdr: (skip) * @handle: File handle to buffer writes to, commit after calling this function * @ihdr: Instrument header structure to store * * Writes an instrument header into @handle from a @ihdr structure. */ void ipatch_sf2_write_ihdr(IpatchFileHandle *handle, const IpatchSF2Ihdr *ihdr) { g_return_if_fail(handle != NULL); g_return_if_fail(ihdr != NULL); ipatch_file_buf_write(handle, ihdr->name, IPATCH_SFONT_NAME_SIZE); ipatch_file_buf_write_u16(handle, ihdr->bag_index); } /** * ipatch_sf2_write_shdr: (skip) * @handle: File handle to buffer writes to, commit after calling this function * @shdr: Sample header structure to store * * Writes a sample header into @handle from a @shdr structure. */ void ipatch_sf2_write_shdr(IpatchFileHandle *handle, const IpatchSF2Shdr *shdr) { g_return_if_fail(handle != NULL); g_return_if_fail(shdr != NULL); ipatch_file_buf_write(handle, shdr->name, IPATCH_SFONT_NAME_SIZE); ipatch_file_buf_write_u32(handle, shdr->start); ipatch_file_buf_write_u32(handle, shdr->end); ipatch_file_buf_write_u32(handle, shdr->loop_start); ipatch_file_buf_write_u32(handle, shdr->loop_end); ipatch_file_buf_write_u32(handle, shdr->rate); ipatch_file_buf_write_u8(handle, shdr->root_note); ipatch_file_buf_write_u8(handle, shdr->fine_tune); ipatch_file_buf_write_u16(handle, shdr->link_index); ipatch_file_buf_write_u16(handle, shdr->type); } /** * ipatch_sf2_write_bag: (skip) * @handle: File handle to buffer writes to, commit after calling this function * @bag: Bag structure to store * * Writes a preset or instrument bag into @handle from a @bag structure. */ void ipatch_sf2_write_bag(IpatchFileHandle *handle, const IpatchSF2Bag *bag) { g_return_if_fail(handle != NULL); g_return_if_fail(bag != NULL); ipatch_file_buf_write_u16(handle, bag->gen_index); ipatch_file_buf_write_u16(handle, bag->mod_index); } /** * ipatch_sf2_write_mod: (skip) * @handle: File handle to buffer writes to, commit after calling this function * @mod: Modulator structure to store * * Writes a modulator into @handle from a @mod structure. */ void ipatch_sf2_write_mod(IpatchFileHandle *handle, const IpatchSF2Mod *mod) { g_return_if_fail(handle != NULL); g_return_if_fail(mod != NULL); ipatch_file_buf_write_u16(handle, mod->src); ipatch_file_buf_write_u16(handle, mod->dest); ipatch_file_buf_write_u16(handle, mod->amount); ipatch_file_buf_write_u16(handle, mod->amtsrc); ipatch_file_buf_write_u16(handle, mod->trans); } /** * ipatch_sf2_write_gen: (skip) * @handle: File handle to buffer writes to, commit after calling this function * @genid: ID of generator to store * @amount: Generator amount to store * * Writes a generator into @handle from a @genid and @amount * structure. */ void ipatch_sf2_write_gen(IpatchFileHandle *handle, int genid, const IpatchSF2GenAmount *amount) { g_return_if_fail(handle != NULL); g_return_if_fail(amount != NULL); ipatch_file_buf_write_u16(handle, genid); /* check if genid is valid (preset or inst) and is a range unit */ if(genid != IPATCH_SF2_GEN_INSTRUMENT_ID && genid != IPATCH_SF2_GEN_SAMPLE_ID && ipatch_sf2_gen_is_valid(genid, FALSE) && ipatch_sf2_gen_info[genid].unit == IPATCH_UNIT_TYPE_RANGE) { /* store the range */ ipatch_file_buf_write_u8(handle, amount->range.low); ipatch_file_buf_write_u8(handle, amount->range.high); } else { ipatch_file_buf_write_s16(handle, amount->sword); } } static gboolean ipatch_sf2_write_level_0(IpatchSF2Writer *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchSF2Inst *inst; IpatchIter iter; guint index; /* write info list */ if(!ipatch_riff_write_chunk(riff, IPATCH_RIFF_CHUNK_LIST, IPATCH_SFONT_FOURCC_INFO, err)) { return (FALSE); } if(!sfont_write_info(writer, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* - Sample data list chunk */ if(!ipatch_riff_write_chunk(riff, IPATCH_RIFF_CHUNK_LIST, IPATCH_SFONT_FOURCC_SDTA, err)) { return (FALSE); } /* if 24 bit samples are enabled write smpl and sm24 chunks */ if(ipatch_item_get_flags(writer->sf) & IPATCH_SF2_SAMPLES_24BIT) { if(!sfont_write_samples24(writer, err)) { return (FALSE); } } else if(!sfont_write_samples(writer, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ /* generate instrument pointer => index hash, used by preset generators */ if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sf), &iter, IPATCH_TYPE_SF2_INST)) { return (FALSE); } inst = ipatch_sf2_inst_first(&iter); index = 1; while(inst) { /* add instrument and index to instrument hash */ g_hash_table_insert(writer->inst_hash, inst, GUINT_TO_POINTER(index)); index++; inst = ipatch_sf2_inst_next(&iter); } /* - SoundFont parameter "Hydra" list chunk */ if(!ipatch_riff_write_list_chunk(riff, IPATCH_SFONT_FOURCC_PDTA, err)) { return (FALSE); } /* - Preset headers */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_SFONT_FOURCC_PHDR, err)) { return (FALSE); } if(!sfont_write_phdrs(writer, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* - Preset bags */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_SFONT_FOURCC_PBAG, err)) { return (FALSE); } if(!sfont_write_pbags(writer, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* - Preset modulators */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_SFONT_FOURCC_PMOD, err)) { return (FALSE); } if(!sfont_write_pmods(writer, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* - Preset generators */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_SFONT_FOURCC_PGEN, err)) { return (FALSE); } if(!sfont_write_pgens(writer, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* - Instrument headers */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_SFONT_FOURCC_INST, err)) { return (FALSE); } if(!sfont_write_ihdrs(writer, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* - Instrument bags */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_SFONT_FOURCC_IBAG, err)) { return (FALSE); } if(!sfont_write_ibags(writer, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* - Instrument modulators */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_SFONT_FOURCC_IMOD, err)) { return (FALSE); } if(!sfont_write_imods(writer, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* - Instrument generators */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_SFONT_FOURCC_IGEN, err)) { return (FALSE); } if(!sfont_write_igens(writer, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* - Sample headers */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_SFONT_FOURCC_SHDR, err)) { return (FALSE); } if(!sfont_write_shdrs(writer, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } return (TRUE); } /* save SoundFont info in the recommended order */ static gboolean sfont_write_info(IpatchSF2Writer *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchSF2Info *info_array; char *val, *software; int i; /* save SoundFont version */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_SFONT_FOURCC_IFIL, err)) { return (FALSE); } ipatch_file_buf_write_u16(riff->handle, writer->sf->ver_major); ipatch_file_buf_write_u16(riff->handle, writer->sf->ver_minor); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* save SoundFont synthesis engine */ if(!(val = ipatch_sf2_get_info(writer->sf, IPATCH_SF2_ENGINE))) { val = g_strdup(IPATCH_SF2_DEFAULT_ENGINE); } if(!sfont_write_strchunk(writer, IPATCH_SFONT_FOURCC_ISNG, val, err)) { g_free(val); return (FALSE); } g_free(val); /* save SoundFont name */ if(!(val = ipatch_sf2_get_info(writer->sf, IPATCH_SF2_NAME))) { val = g_strdup(IPATCH_BASE_DEFAULT_NAME); } if(!sfont_write_strchunk(writer, IPATCH_SFONT_FOURCC_INAM, val, err)) { g_free(val); return (FALSE); } g_free(val); /* SoundFont has ROM name set? */ if((val = ipatch_sf2_get_info(writer->sf, IPATCH_SF2_ROM_NAME))) { /* save ROM name */ if(!sfont_write_strchunk(writer, IPATCH_SFONT_FOURCC_IROM, val, err)) { g_free(val); return (FALSE); } g_free(val); /* save the ROM version too */ if(!ipatch_riff_write_sub_chunk(riff, IPATCH_SFONT_FOURCC_IVER, err)) { return (FALSE); } ipatch_file_buf_write_u16(riff->handle, writer->sf->romver_major); ipatch_file_buf_write_u16(riff->handle, writer->sf->romver_minor); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } } /* write the rest of the info (except ones already written) */ info_array = ipatch_sf2_get_info_array(writer->sf); for(i = 0; info_array[i].id; i++) { if(info_array[i].id != IPATCH_SF2_ENGINE && info_array[i].id != IPATCH_SF2_NAME && info_array[i].id != IPATCH_SF2_ROM_NAME && info_array[i].id != IPATCH_SF2_SOFTWARE) if(!sfont_write_strchunk(writer, info_array[i].id, info_array[i].val, err)) { ipatch_sf2_free_info_array(info_array); return (FALSE); } } ipatch_sf2_free_info_array(info_array); /* compose the libInstPatch software string using application name if any */ if(ipatch_application_name) software = g_strconcat(ipatch_application_name, " (libInstPatch " IPATCH_VERSION ")", NULL); else { software = g_strdup("libInstPatch " IPATCH_VERSION); } /* construct software created:modified string */ val = ipatch_sf2_get_info(writer->sf, IPATCH_SF2_SOFTWARE); if(val) { char *tmp; tmp = strchr(val, ':'); /* find colon created:modified separator */ if(tmp) { tmp[1] = '\0'; /* terminate after : */ tmp = g_strconcat(val, software, NULL); g_free(val); val = tmp; } else /* no colon separator? Discard.. */ { g_free(val); val = g_strdup(software); } } else { val = g_strdup(software); } g_free(software); /* write the software string */ if(!sfont_write_strchunk(writer, IPATCH_SFONT_FOURCC_ISFT, val, err)) { g_free(val); return (FALSE); } g_free(val); return (TRUE); } /* write a even size null terminated string contained in a sub chunk */ static gboolean sfont_write_strchunk(IpatchSF2Writer *writer, guint32 id, const char *val, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); char pad = '\0'; int len; if(!ipatch_riff_write_sub_chunk(riff, id, err)) { return (FALSE); } len = strlen(val) + 1; if(!ipatch_file_write(riff->handle, val, len, err)) { return (FALSE); } if(len & 1) /* pad to an even number of bytes */ if(!ipatch_file_write(riff->handle, &pad, 1, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } return (TRUE); } /* save sample data (16 bit mode) */ static gboolean sfont_write_samples(IpatchSF2Writer *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchSF2Sample *sample; IpatchSampleHandle handle; IpatchSF2File *file = IPATCH_SF2_FILE(riff->handle->file); SampleHashValue *sample_hash_value; IpatchRiffChunk *chunk; IpatchIter iter; gpointer buf; guint8 zerobuf[46 * 2]; /* 46 zero values to write at end of each sample */ guint samsize, size, ofs; int index = 0; memset(&zerobuf, 0, sizeof(zerobuf)); /* - Sample data sub chunk */ if(!ipatch_riff_write_chunk(riff, IPATCH_RIFF_CHUNK_SUB, IPATCH_SFONT_FOURCC_SMPL, err)) { return (FALSE); } /* set the sample position in the IpatchSF2File */ ipatch_sf2_file_set_sample_pos(file, ipatch_riff_get_position(riff)); if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sf), &iter, IPATCH_TYPE_SF2_SAMPLE)) { return (FALSE); } /* traverse samples */ for(sample = ipatch_sf2_sample_first(&iter); sample; sample = ipatch_sf2_sample_next(&iter)) { /* add sample info to sample hash */ sample_hash_value = ipatch_sf2_writer_sample_hash_value_new(); sample_hash_value->index = index++; g_hash_table_insert(writer->sample_hash, sample, sample_hash_value); /* ignore ROM samples */ if(ipatch_item_get_flags(sample) & IPATCH_SF2_SAMPLE_FLAG_ROM) { continue; } /* get sample position in sample chunk and store to sample hash value */ chunk = ipatch_riff_get_chunk(riff, -1); sample_hash_value->position = file->sample_pos + chunk->position; /* ++ open sample handle */ if(!ipatch_sample_data_open_native_sample(sample->sample_data, &handle, 'r', FORMAT_16BIT, IPATCH_SAMPLE_UNITY_CHANNEL_MAP, err)) { return (FALSE); } size = ipatch_sample_handle_get_max_frames(&handle); samsize = ipatch_sample_get_size((IpatchSample *)(sample->sample_data), NULL); for(ofs = 0; ofs < samsize; ofs += size) /* loop while data to store */ { if(samsize - ofs < size) /* check for last partial fragment */ { size = samsize - ofs; } /* read and transform (if necessary) audio data from sample store */ if(!(buf = ipatch_sample_handle_read(&handle, ofs, size, NULL, err))) { ipatch_sample_handle_close(&handle); /* -- close sample handle */ return (FALSE); } /* write 16 bit mono sample data to SoundFont file */ if(!ipatch_file_write(riff->handle, buf, size * 2, err)) { ipatch_sample_handle_close(&handle); /* -- close sample handle */ return (FALSE); } } ipatch_sample_handle_close(&handle); /* -- close sample handle */ /* 46 "zero" samples following sample as per SoundFont spec */ if(!ipatch_file_write(riff->handle, &zerobuf, 46 * 2, err)) { return (FALSE); } } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } /* */ return (TRUE); } /* save sample data (24 bit mode) */ static gboolean sfont_write_samples24(IpatchSF2Writer *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchSF2Sample *sample; IpatchSampleHandle handle; IpatchSF2File *file = IPATCH_SF2_FILE(riff->handle->file); SampleHashValue *sample_hash_value; IpatchIter iter; gpointer buf; guint8 *lsbuf = NULL; /* init to NULL for error: return */ guint lsbuf_size = 0; guint8 zerobuf[46 * 2]; /* 46 zero values to write at end of each sample */ guint samsize, size, start, ofs, total_size, totalofs = 0; guint index = 0; guint i; memset(&zerobuf, 0, sizeof(zerobuf)); /* - Sample data sub chunk */ if(!ipatch_riff_write_chunk(riff, IPATCH_RIFF_CHUNK_SUB, IPATCH_SFONT_FOURCC_SMPL, err)) { goto error; } /* set the sample position in the IpatchSF2File */ ipatch_sf2_file_set_sample_pos(file, ipatch_riff_get_position(riff)); if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sf), &iter, IPATCH_TYPE_SF2_SAMPLE)) { goto error; } /* calc total size of smpl chunk, so we can write sm24 chunk simultaneously */ sample = ipatch_sf2_sample_first(&iter); for(total_size = 0; sample; sample = ipatch_sf2_sample_next(&iter)) { /* ignore ROM samples */ if(ipatch_item_get_flags(sample) & IPATCH_SF2_SAMPLE_FLAG_ROM) { continue; } /* size of audio in samples + 46 silent samples */ total_size += ipatch_sample_get_size((IpatchSample *)sample, NULL) + 46; } /* seek to end of smpl chunk (sample data written later) */ if(!ipatch_file_seek(riff->handle, total_size * 2, G_SEEK_CUR, err)) { return (FALSE); } if(!ipatch_riff_close_chunk(riff, -1, err)) { goto error; } /* */ /* - LS bytes of 24 bit sample data sub chunk */ if(!ipatch_riff_write_chunk(riff, IPATCH_RIFF_CHUNK_SUB, IPATCH_SFONT_FOURCC_SM24, err)) { return (FALSE); } /* set the sample 24 position in the IpatchSF2File */ ipatch_sf2_file_set_sample24_pos(file, ipatch_riff_get_position(riff)); /* allocate extra buffer to store LS bytes of 24 bit samples */ lsbuf = g_malloc(IPATCH_SAMPLE_COPY_BUFFER_SIZE); /* traverse samples */ for(sample = ipatch_sf2_sample_first(&iter); sample; sample = ipatch_sf2_sample_next(&iter)) { /* add sample info to sample hash */ sample_hash_value = ipatch_sf2_writer_sample_hash_value_new(); sample_hash_value->index = index++; g_hash_table_insert(writer->sample_hash, sample, sample_hash_value); /* ignore ROM samples */ if(ipatch_item_get_flags(sample) & IPATCH_SF2_SAMPLE_FLAG_ROM) { continue; } /* ++ open sample handle */ if(!ipatch_sample_data_open_native_sample(sample->sample_data, &handle, 'r', FORMAT_24BIT, IPATCH_SAMPLE_UNITY_CHANNEL_MAP, err)) { goto error; } size = ipatch_sample_handle_get_max_frames(&handle); samsize = ipatch_sample_get_size((IpatchSample *)(sample->sample_data), NULL); /* Allocate/reallocate 24 bit LSB buffer (1 byte per 24 bit sample) */ if(size > lsbuf_size) { lsbuf = g_realloc(lsbuf, size); lsbuf_size = size; } /* start offset in samples of this sample data */ start = totalofs; sample_hash_value->position = file->sample_pos + start * 2; sample_hash_value->position24 = file->sample24_pos + start; /* loop while data to store */ for(ofs = 0; ofs < samsize; ofs += size, totalofs += size) { if(samsize - ofs < size) /* check for last partial fragment */ { size = samsize - ofs; } /* read and transform (if necessary) audio data from sample store */ if(!(buf = ipatch_sample_handle_read(&handle, ofs, size, NULL, err))) { goto error_close_handle; } /* copy the LS bytes of the 24 bit samples */ for(i = 0; i < size; i++) { ((guint8 *)lsbuf)[i] = ((guint8 *)buf)[i * 4]; } /* compact the 16 bit portion of the 24 bit samples */ for(i = 0; i < size; i++) { ((guint8 *)buf)[i * 2] = ((guint8 *)buf)[i * 4 + 1]; ((guint8 *)buf)[i * 2 + 1] = ((guint8 *)buf)[i * 4 + 2]; } /* seek to location in smpl chunk to store 16 bit data */ if(!ipatch_file_seek(riff->handle, file->sample_pos + totalofs * 2, G_SEEK_SET, err)) { goto error_close_handle; } /* write 16 bit portions of 24 bit samples to file */ if(!ipatch_file_write(riff->handle, buf, size * 2, err)) { goto error_close_handle; } /* seek to location in sm24 chunk to store LS bytes of 24 bit samples */ if(!ipatch_file_seek(riff->handle, file->sample24_pos + totalofs, G_SEEK_SET, err)) { goto error_close_handle; } /* write least significant 8 bits of 24 bit samples to file */ if(!ipatch_file_write(riff->handle, lsbuf, size, err)) { goto error_close_handle; } } ipatch_sample_handle_close(&handle); /* -- close sample handle */ /* seek to location in smpl chunk to store 16 bit zero samples */ if(!ipatch_file_seek(riff->handle, file->sample_pos + totalofs * 2, G_SEEK_SET, err)) { goto error; } /* 46 "zero" samples following sample as per SoundFont spec */ if(!ipatch_file_write(riff->handle, &zerobuf, 46 * 2, err)) { goto error; } /* seek to location in sm24 chunk to store LS bytes of zero samples */ if(!ipatch_file_seek(riff->handle, file->sample24_pos + totalofs, G_SEEK_SET, err)) { goto error; } /* 46 "zero" samples following sample as per SoundFont spec */ if(!ipatch_file_write(riff->handle, &zerobuf, 46, err)) { goto error; } totalofs += 46; } g_free(lsbuf); /* free lsbuf */ /* must set to NULL in case of error: return */ lsbuf = NULL; /* seek to end of sm24 chunk */ if(!ipatch_file_seek(riff->handle, file->sample24_pos + total_size, G_SEEK_SET, err)) { goto error; } if(!ipatch_riff_close_chunk(riff, -1, err)) { goto error; } /* */ return (TRUE); error_close_handle: ipatch_sample_handle_close(&handle); /* -- close sample handle */ error: g_free(lsbuf); /* free lsbuf */ return (FALSE); } /* save preset headers */ static gboolean sfont_write_phdrs(IpatchSF2Writer *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchSF2Preset *preset; IpatchSF2Phdr phdr; IpatchIter iter, zone_iter; guint16 pbagndx = 0; if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sf), &iter, IPATCH_TYPE_SF2_PRESET)) { return (FALSE); } preset = ipatch_sf2_preset_first(&iter); while(preset) /* loop over all presets */ { strncpy(phdr.name, preset->name, IPATCH_SFONT_NAME_SIZE); phdr.program = preset->program; phdr.bank = preset->bank; phdr.bag_index = pbagndx; phdr.library = preset->library; phdr.genre = preset->genre; phdr.morphology = preset->morphology; ipatch_sf2_write_phdr(riff->handle, &phdr); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } /* get count of preset zones */ if(!ipatch_container_init_iter((IpatchContainer *)preset, &zone_iter, IPATCH_TYPE_SF2_PZONE)) { return (FALSE); } pbagndx += ipatch_iter_count(&zone_iter); /* if any global generators or modulators then add 1 for global zone */ if(preset->genarray.flags || preset->mods) { pbagndx++; } preset = ipatch_sf2_preset_next(&iter); /* next preset */ } /* create terminal record */ memset(&phdr, 0, sizeof(phdr)); strcpy(phdr.name, "EOP"); phdr.bag_index = pbagndx; ipatch_sf2_write_phdr(riff->handle, &phdr); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } /* save preset bags */ static gboolean sfont_write_pbags(IpatchSF2Writer *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchSF2Preset *preset; IpatchSF2Zone *zone; IpatchSF2Bag bag; IpatchIter iter, zone_iter; guint16 genndx = 0, modndx = 0; if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sf), &iter, IPATCH_TYPE_SF2_PRESET)) { return (FALSE); } preset = ipatch_sf2_preset_first(&iter); while(preset) /* traverse through presets */ { if(!ipatch_container_init_iter((IpatchContainer *)preset, &zone_iter, IPATCH_TYPE_SF2_PZONE)) { return (FALSE); } /* process global zone if any global modulators or generators */ if(preset->genarray.flags || preset->mods) { zone = NULL; } else { zone = ipatch_sf2_zone_first(&zone_iter); } do { bag.gen_index = genndx; bag.mod_index = modndx; ipatch_sf2_write_bag(riff->handle, &bag); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } if(zone) { genndx += ipatch_sf2_gen_array_count_set(&zone->genarray); if(zone->item) { genndx++; /* increment for INSTRUMENT_ID */ } modndx += g_slist_length(zone->mods); } else /* after global zone */ { genndx += ipatch_sf2_gen_array_count_set(&preset->genarray); modndx += g_slist_length(preset->mods); } if(!zone) { zone = ipatch_sf2_zone_first(&zone_iter); /* after global zone */ } else { zone = ipatch_sf2_zone_next(&zone_iter); } } while(zone); /* traverse preset's zones */ preset = ipatch_sf2_preset_next(&iter); } /* terminal record */ bag.gen_index = genndx; bag.mod_index = modndx; ipatch_sf2_write_bag(riff->handle, &bag); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } /* save preset modulators */ static gboolean sfont_write_pmods(IpatchSF2Writer *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchSF2Preset *preset; IpatchSF2Zone *zone; IpatchSF2Mod *mod; IpatchIter iter, zone_iter; GSList *p; if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sf), &iter, IPATCH_TYPE_SF2_PRESET)) { return (FALSE); } preset = ipatch_sf2_preset_first(&iter); while(preset) /* traverse through all presets */ { zone = NULL; p = preset->mods; /* first is the global modulators */ if(!ipatch_container_init_iter((IpatchContainer *)preset, &zone_iter, IPATCH_TYPE_SF2_PZONE)) { return (FALSE); } do { while(p) /* save zone's modulators */ { mod = (IpatchSF2Mod *)(p->data); ipatch_sf2_write_mod(riff->handle, mod); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } p = g_slist_next(p); } if(!zone) { zone = ipatch_sf2_zone_first(&zone_iter); /* after global zone */ } else { zone = ipatch_sf2_zone_next(&zone_iter); } if(zone) { p = zone->mods; } } while(zone); /* traverse this preset's zones */ preset = ipatch_sf2_preset_next(&iter); } /* terminal record */ ipatch_file_buf_zero(riff->handle, IPATCH_SFONT_MOD_SIZE); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } /* save preset generators */ static gboolean sfont_write_pgens(IpatchSF2Writer *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchSF2Preset *preset; IpatchSF2Zone *zone; IpatchSF2GenArray *genarray; IpatchSF2GenAmount amount; IpatchIter iter, zone_iter; guint inst_index; guint64 flags; int i; if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sf), &iter, IPATCH_TYPE_SF2_PRESET)) { return (FALSE); } preset = ipatch_sf2_preset_first(&iter); while(preset) /* traverse through all presets */ { /* global zone */ genarray = &preset->genarray; zone = NULL; if(!ipatch_container_init_iter((IpatchContainer *)preset, &zone_iter, IPATCH_TYPE_SF2_PZONE)) { return (FALSE); } do { if(IPATCH_SF2_GEN_ARRAY_TEST_FLAG /* note range set? */ (genarray, IPATCH_SF2_GEN_NOTE_RANGE)) { ipatch_sf2_write_gen(riff->handle, IPATCH_SF2_GEN_NOTE_RANGE, &genarray->values [IPATCH_SF2_GEN_NOTE_RANGE]); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } } if(IPATCH_SF2_GEN_ARRAY_TEST_FLAG /* velocity range set? */ (genarray, IPATCH_SF2_GEN_VELOCITY_RANGE)) { ipatch_sf2_write_gen(riff->handle, IPATCH_SF2_GEN_VELOCITY_RANGE, &genarray->values [IPATCH_SF2_GEN_VELOCITY_RANGE]); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } } /* clear the note range and velocity since already saved */ flags = genarray->flags & ~(IPATCH_SF2_GENID_SET(IPATCH_SF2_GEN_NOTE_RANGE) | IPATCH_SF2_GENID_SET(IPATCH_SF2_GEN_VELOCITY_RANGE)); /* set the rest of the generators */ for(i = 0; flags != 0; i++, flags >>= 1) { if(flags & 0x1) /* generator set? */ { ipatch_sf2_write_gen(riff->handle, i, &genarray->values[i]); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } } } /* save instrument ID if any */ if(zone && zone->item) { inst_index = GPOINTER_TO_UINT (g_hash_table_lookup(writer->inst_hash, zone->item)); g_return_val_if_fail(inst_index != 0, FALSE); inst_index--; /* index + 1 (to catch NULL), so decrement */ amount.uword = inst_index; ipatch_sf2_write_gen(riff->handle, IPATCH_SF2_GEN_INSTRUMENT_ID, &amount); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } } if(!zone) { zone = ipatch_sf2_zone_first(&zone_iter); /* after global zone */ } else { zone = ipatch_sf2_zone_next(&zone_iter); /* next zone */ } if(zone) { genarray = &zone->genarray; } } while(zone); /* traverse preset's zones */ preset = ipatch_sf2_preset_next(&iter); /* next preset */ } /* terminal record */ ipatch_file_buf_zero(riff->handle, IPATCH_SFONT_GEN_SIZE); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } /* save instrument headers */ static gboolean sfont_write_ihdrs(IpatchSF2Writer *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchSF2Inst *inst; IpatchSF2Ihdr ihdr; IpatchIter iter, zone_iter; guint16 ibagndx = 0; if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sf), &iter, IPATCH_TYPE_SF2_INST)) { return (FALSE); } inst = ipatch_sf2_inst_first(&iter); while(inst) /* loop over all instruments */ { strncpy(ihdr.name, inst->name, IPATCH_SFONT_NAME_SIZE); ihdr.bag_index = ibagndx; ipatch_sf2_write_ihdr(riff->handle, &ihdr); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } if(!ipatch_container_init_iter((IpatchContainer *)inst, &zone_iter, IPATCH_TYPE_SF2_IZONE)) { return (FALSE); } ibagndx += ipatch_iter_count(&zone_iter); /* if any global generators or modulators then add 1 for global zone */ if(inst->genarray.flags || inst->mods) { ibagndx++; } inst = ipatch_sf2_inst_next(&iter); } /* terminal record */ memset(&ihdr, 0, sizeof(ihdr)); strcpy(ihdr.name, "EOI"); ihdr.bag_index = ibagndx; ipatch_sf2_write_ihdr(riff->handle, &ihdr); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } /* save instrument bags */ static gboolean sfont_write_ibags(IpatchSF2Writer *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchSF2Inst *inst; IpatchSF2Zone *zone; IpatchSF2Bag bag; IpatchIter iter, zone_iter; guint16 genndx = 0, modndx = 0; if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sf), &iter, IPATCH_TYPE_SF2_INST)) { return (FALSE); } inst = ipatch_sf2_inst_first(&iter); while(inst) /* traverse through instruments */ { if(!ipatch_container_init_iter((IpatchContainer *)inst, &zone_iter, IPATCH_TYPE_SF2_IZONE)) { return (FALSE); } /* process global zone if any global modulators or generators */ if(inst->genarray.flags || inst->mods) { zone = NULL; } else { zone = ipatch_sf2_zone_first(&zone_iter); } do { bag.gen_index = genndx; bag.mod_index = modndx; ipatch_sf2_write_bag(riff->handle, &bag); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } if(zone) { genndx += ipatch_sf2_gen_array_count_set(&zone->genarray); if(zone->item) { genndx++; /* increment for SAMPLE_ID */ } modndx += g_slist_length(zone->mods); } else /* after global zone */ { genndx += ipatch_sf2_gen_array_count_set(&inst->genarray); modndx += g_slist_length(inst->mods); } if(!zone) { zone = ipatch_sf2_zone_first(&zone_iter); /* after global zone */ } else { zone = ipatch_sf2_zone_next(&zone_iter); } } while(zone); /* traverse instrument's zones */ inst = ipatch_sf2_inst_next(&iter); } /* terminal record */ bag.gen_index = genndx; bag.mod_index = modndx; ipatch_sf2_write_bag(riff->handle, &bag); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } /* save instrument modulators */ static gboolean sfont_write_imods(IpatchSF2Writer *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchSF2Inst *inst; IpatchSF2Zone *zone; IpatchSF2Mod *mod; IpatchIter iter, zone_iter; GSList *p; if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sf), &iter, IPATCH_TYPE_SF2_INST)) { return (FALSE); } inst = ipatch_sf2_inst_first(&iter); while(inst) /* traverse through all instruments */ { zone = NULL; p = inst->mods; /* first is the global modulators */ if(!ipatch_container_init_iter((IpatchContainer *)inst, &zone_iter, IPATCH_TYPE_SF2_IZONE)) { return (FALSE); } do { while(p) /* save modulators */ { mod = (IpatchSF2Mod *)(p->data); ipatch_sf2_write_mod(riff->handle, mod); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } p = g_slist_next(p); } if(!zone) { zone = ipatch_sf2_zone_first(&zone_iter); /* after global zone */ } else { zone = ipatch_sf2_zone_next(&zone_iter); } if(zone) { p = zone->mods; } } while(zone); /* traverse this instrument's zones */ inst = ipatch_sf2_inst_next(&iter); } /* terminal record */ ipatch_file_buf_zero(riff->handle, IPATCH_SFONT_MOD_SIZE); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } /* save instrument generators */ static gboolean sfont_write_igens(IpatchSF2Writer *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchSF2Inst *inst; IpatchSF2Zone *zone; IpatchSF2GenArray *genarray; IpatchSF2GenAmount amount; IpatchIter iter, zone_iter; SampleHashValue *sample_hash_value; guint64 flags; int i; if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sf), &iter, IPATCH_TYPE_SF2_INST)) { return (FALSE); } inst = ipatch_sf2_inst_first(&iter); while(inst) /* traverse through all instruments */ { /* global zone */ genarray = &inst->genarray; zone = NULL; if(!ipatch_container_init_iter((IpatchContainer *)inst, &zone_iter, IPATCH_TYPE_SF2_IZONE)) { return (FALSE); } do { if(IPATCH_SF2_GEN_ARRAY_TEST_FLAG /* note range set? */ (genarray, IPATCH_SF2_GEN_NOTE_RANGE)) { ipatch_sf2_write_gen(riff->handle, IPATCH_SF2_GEN_NOTE_RANGE, &genarray->values [IPATCH_SF2_GEN_NOTE_RANGE]); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } } if(IPATCH_SF2_GEN_ARRAY_TEST_FLAG /* velocity range set? */ (genarray, IPATCH_SF2_GEN_VELOCITY_RANGE)) { ipatch_sf2_write_gen(riff->handle, IPATCH_SF2_GEN_VELOCITY_RANGE, &genarray->values [IPATCH_SF2_GEN_VELOCITY_RANGE]); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } } /* clear the note range and velocity since already saved */ flags = genarray->flags & ~(IPATCH_SF2_GENID_SET(IPATCH_SF2_GEN_NOTE_RANGE) | IPATCH_SF2_GENID_SET(IPATCH_SF2_GEN_VELOCITY_RANGE)); /* set the rest of the generators */ for(i = 0; flags != 0; i++, flags >>= 1) { if(flags & 0x1) /* generator set? */ { ipatch_sf2_write_gen(riff->handle, i, &genarray->values[i]); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } } } /* save sample ID if any */ if(zone && zone->item) { sample_hash_value = g_hash_table_lookup(writer->sample_hash, zone->item); g_return_val_if_fail(sample_hash_value != NULL, FALSE); amount.uword = sample_hash_value->index; ipatch_sf2_write_gen(riff->handle, IPATCH_SF2_GEN_SAMPLE_ID, &amount); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } } if(!zone) { zone = ipatch_sf2_zone_first(&zone_iter); /* after global zone */ } else { zone = ipatch_sf2_zone_next(&zone_iter); /* next zone */ } if(zone) { genarray = &zone->genarray; } } while(zone); /* traverse instrument's zones */ inst = ipatch_sf2_inst_next(&iter); /* next instrument */ } /* terminal record */ ipatch_file_buf_zero(riff->handle, IPATCH_SFONT_GEN_SIZE); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } /* save sample headers */ static gboolean sfont_write_shdrs(IpatchSF2Writer *writer, GError **err) { IpatchRiff *riff = IPATCH_RIFF(writer); IpatchSF2File *sf2file = IPATCH_SF2_FILE(riff->handle->file); SampleHashValue *sample_hash_value; IpatchSF2Sample *sample, *linked; IpatchIter iter; IpatchSampleStore *store; IpatchSF2Shdr shdr; guint start; int location; int untitled = 0; if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sf), &iter, IPATCH_TYPE_SF2_SAMPLE)) { return (FALSE); } /* traverse all samples */ for(sample = ipatch_sf2_sample_first(&iter); sample; sample = ipatch_sf2_sample_next(&iter)) { if(!(ipatch_item_get_flags(sample) & IPATCH_SF2_SAMPLE_FLAG_ROM)) { sample_hash_value = g_hash_table_lookup(writer->sample_hash, sample); g_return_val_if_fail(sample_hash_value != NULL, FALSE); start = sample_hash_value->position; start -= sf2file->sample_pos; /* use offset from start of samples */ shdr.type = 0; } else /* ROM sample */ { store = ipatch_sample_data_get_native_sample(sample->sample_data); g_return_val_if_fail(store != NULL, FALSE); g_object_get(store, "location", &location, NULL); start = location; shdr.type = IPATCH_SF2_FILE_SAMPLE_TYPE_ROM; } memset(shdr.name, 0, IPATCH_SFONT_NAME_SIZE); if(sample->name) { strncpy(shdr.name, sample->name, IPATCH_SFONT_NAME_SIZE - 1); } else { sprintf(shdr.name, _("untitled-%d"), ++untitled); /* i18n: Should be less than 16 chars! */ } start /= 2; /* convert start from bytes to samples */ shdr.start = start; shdr.end = ipatch_sample_get_size((IpatchSample *)sample, NULL) + start; shdr.loop_start = sample->loop_start + start; shdr.loop_end = sample->loop_end + start; shdr.rate = sample->rate; shdr.root_note = sample->root_note; shdr.fine_tune = sample->fine_tune; switch(sample->channel) { case IPATCH_SF2_SAMPLE_CHANNEL_LEFT: shdr.type |= IPATCH_SF2_FILE_SAMPLE_TYPE_LEFT; break; case IPATCH_SF2_SAMPLE_CHANNEL_RIGHT: shdr.type |= IPATCH_SF2_FILE_SAMPLE_TYPE_RIGHT; break; default: shdr.type |= IPATCH_SF2_FILE_SAMPLE_TYPE_MONO; break; } shdr.link_index = 0; linked = ipatch_sf2_sample_peek_linked(sample); if(linked) { sample_hash_value = g_hash_table_lookup(writer->sample_hash, linked); g_return_val_if_fail(sample_hash_value != NULL, FALSE); shdr.link_index = sample_hash_value->index; } ipatch_sf2_write_shdr(riff->handle, &shdr); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } } /* terminal record */ memset(&shdr, 0, IPATCH_SFONT_SHDR_SIZE); strcpy(shdr.name, "EOS"); ipatch_sf2_write_shdr(riff->handle, &shdr); if(!ipatch_file_buf_commit(riff->handle, err)) { return (FALSE); } return (TRUE); } libinstpatch-1.1.6/libinstpatch/IpatchSF2Writer.h000066400000000000000000000065541400263525300217720ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_WRITER_H__ #define __IPATCH_SF2_WRITER_H__ #include #include #include #include #include #include #include typedef struct _IpatchSF2Writer IpatchSF2Writer; /* private structure */ typedef struct _IpatchSF2WriterClass IpatchSF2WriterClass; #define IPATCH_TYPE_SF2_WRITER (ipatch_sf2_writer_get_type ()) #define IPATCH_SF2_WRITER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SF2_WRITER, \ IpatchSF2Writer)) #define IPATCH_SF2_WRITER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SF2_WRITER, \ IpatchSF2WriterClass)) #define IPATCH_IS_SF2_WRITER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SF2_WRITER)) #define IPATCH_IS_SF2_WRITER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SF2_WRITER)) /* SF2 writer object */ struct _IpatchSF2Writer { IpatchRiff parent_instance; /* derived from IpatchRiff */ IpatchSF2 *orig_sf; /* original SF2 object */ IpatchSF2 *sf; /* duplicated SF2 object to save */ gboolean migrate_samples; /* set to TRUE to migrate samples to new file */ GHashTable *inst_hash; /* instrument => index hash */ GHashTable *sample_hash; /* sample => SampleHashValue hash */ IpatchList *store_list; /* list of stores, only set if ipatch_sf2_writer_create_stores() was called */ }; /* SF2 writer class */ struct _IpatchSF2WriterClass { IpatchRiffClass parent_class; }; GType ipatch_sf2_writer_get_type(void); IpatchSF2Writer *ipatch_sf2_writer_new(IpatchFileHandle *handle, IpatchSF2 *sfont); void ipatch_sf2_writer_set_patch(IpatchSF2Writer *writer, IpatchSF2 *sfont); void ipatch_sf2_writer_set_file_handle(IpatchSF2Writer *writer, IpatchFileHandle *handle); gboolean ipatch_sf2_writer_save(IpatchSF2Writer *writer, GError **err); IpatchList *ipatch_sf2_writer_create_stores(IpatchSF2Writer *writer); void ipatch_sf2_write_phdr(IpatchFileHandle *handle, const IpatchSF2Phdr *phdr); void ipatch_sf2_write_ihdr(IpatchFileHandle *handle, const IpatchSF2Ihdr *ihdr); void ipatch_sf2_write_shdr(IpatchFileHandle *handle, const IpatchSF2Shdr *shdr); void ipatch_sf2_write_bag(IpatchFileHandle *handle, const IpatchSF2Bag *bag); void ipatch_sf2_write_mod(IpatchFileHandle *handle, const IpatchSF2Mod *mod); void ipatch_sf2_write_gen(IpatchFileHandle *handle, int genid, const IpatchSF2GenAmount *amount); #endif libinstpatch-1.1.6/libinstpatch/IpatchSF2Zone.c000066400000000000000000000333701400263525300214200ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSF2Zone * @short_description: Abstract base class for SoundFont zones * @see_also: * @stability: Stable * * Zones are children of #IpatchSF2Preset and #IpatchSF2Inst and define * synthesis parameters and a linked item (#IpatchSF2Inst in the case of * #IpatchSF2PZone and #IpatchSF2Sample in the case of #IpatchSF2IZone). */ #include #include #include #include #include #include "IpatchSF2Zone.h" #include "IpatchSF2PZone.h" #include "IpatchSF2.h" #include "IpatchSF2Gen.h" #include "IpatchSF2Mod.h" #include "IpatchSF2ModItem.h" #include "IpatchParamProp.h" #include "IpatchRange.h" #include "IpatchUnit.h" #include "builtin_enums.h" #include "ipatch_priv.h" static void ipatch_sf2_zone_mod_item_iface_init (IpatchSF2ModItemIface *moditem_iface); static void ipatch_sf2_zone_class_init(IpatchSF2ZoneClass *klass); static void ipatch_sf2_zone_finalize(GObject *gobject); static void ipatch_sf2_zone_get_title(IpatchSF2Zone *zone, GValue *value); static void ipatch_sf2_zone_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sf2_zone_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sf2_zone_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static void ipatch_sf2_zone_item_remove_full(IpatchItem *item, gboolean full); static void ipatch_sf2_zone_link_item_title_notify(IpatchItemPropNotify *info); /* generator property IDs go before these */ enum { PROP_0, PROP_TITLE, PROP_MODULATORS }; /* For passing between class init and mod item interface init */ static GParamSpec *modulators_spec = NULL; G_DEFINE_ABSTRACT_TYPE_WITH_CODE(IpatchSF2Zone, ipatch_sf2_zone, IPATCH_TYPE_ITEM, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SF2_MOD_ITEM, ipatch_sf2_zone_mod_item_iface_init)) static void ipatch_sf2_zone_class_init(IpatchSF2ZoneClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->finalize = ipatch_sf2_zone_finalize; obj_class->get_property = ipatch_sf2_zone_get_property; item_class->copy = ipatch_sf2_zone_item_copy; item_class->item_set_property = ipatch_sf2_zone_set_property; item_class->remove_full = ipatch_sf2_zone_item_remove_full; g_object_class_override_property(obj_class, PROP_TITLE, "title"); g_object_class_override_property(obj_class, PROP_MODULATORS, "modulators"); modulators_spec = g_object_class_find_property(obj_class, "modulators"); } /* mod item interface initialization */ static void ipatch_sf2_zone_mod_item_iface_init(IpatchSF2ModItemIface *moditem_iface) { moditem_iface->modlist_ofs = G_STRUCT_OFFSET(IpatchSF2Zone, mods); /* cache the modulators property for fast notifications */ moditem_iface->mod_pspec = modulators_spec; } static void ipatch_sf2_zone_init(IpatchSF2Zone *zone) { } static void ipatch_sf2_zone_finalize(GObject *gobject) { IpatchSF2Zone *zone = IPATCH_SF2_ZONE(gobject); IpatchItem *link = NULL; GSList *p; IPATCH_ITEM_WLOCK(zone); if(zone->item) { link = zone->item; g_object_unref(zone->item); zone->item = NULL; } p = zone->mods; while(p) { ipatch_sf2_mod_free((IpatchSF2Mod *)(p->data)); p = g_slist_next(p); } g_slist_free(zone->mods); zone->mods = NULL; IPATCH_ITEM_WUNLOCK(zone); if(link) ipatch_item_prop_disconnect_matched(link, ipatch_item_pspec_title, ipatch_sf2_zone_link_item_title_notify, zone); if(G_OBJECT_CLASS(ipatch_sf2_zone_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_sf2_zone_parent_class)->finalize(gobject); } } static void ipatch_sf2_zone_get_title(IpatchSF2Zone *zone, GValue *value) { IpatchItem *ref; char *s = NULL; g_object_get(zone, "link-item", &ref, NULL); /* ++ ref zone linked item */ if(ref) { g_object_get(ref, "name", &s, NULL); g_object_unref(ref); /* -- unref zone linked item */ } g_value_take_string(value, s); } static void ipatch_sf2_zone_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSF2Zone *zone = IPATCH_SF2_ZONE(object); IpatchSF2ModList *list; if(property_id == PROP_MODULATORS) { list = (IpatchSF2ModList *)g_value_get_boxed(value); ipatch_sf2_mod_item_set_mods(IPATCH_SF2_MOD_ITEM(zone), list, IPATCH_SF2_MOD_NO_NOTIFY); } else { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_sf2_zone_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSF2Zone *zone = IPATCH_SF2_ZONE(object); IpatchSF2ModList *list; if(property_id == PROP_TITLE) { ipatch_sf2_zone_get_title(zone, value); } else if(property_id == PROP_MODULATORS) { list = ipatch_sf2_mod_item_get_mods(IPATCH_SF2_MOD_ITEM(zone)); g_value_take_boxed(value, list); } else { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_sf2_zone_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchSF2Zone *src_zone, *dest_zone; IpatchItem *refitem; IpatchSF2Mod *mod; GSList *p; src_zone = IPATCH_SF2_ZONE(src); dest_zone = IPATCH_SF2_ZONE(dest); IPATCH_ITEM_RLOCK(src_zone); refitem = IPATCH_ITEM_COPY_LINK_FUNC(dest, src_zone->item, link_func, user_data); if(refitem) { ipatch_sf2_zone_set_link_item(dest_zone, refitem); } p = src_zone->mods; while(p) /* duplicate modulators */ { mod = ipatch_sf2_mod_duplicate((IpatchSF2Mod *)(p->data)); dest_zone->mods = g_slist_prepend(dest_zone->mods, mod); p = g_slist_next(p); } /* duplicate generator array */ memcpy(&dest_zone->genarray, &src_zone->genarray, sizeof(IpatchSF2GenArray)); IPATCH_ITEM_RUNLOCK(src_zone); dest_zone->mods = g_slist_reverse(dest_zone->mods); } static void ipatch_sf2_zone_item_remove_full(IpatchItem *item, gboolean full) { if(full) { ipatch_sf2_zone_set_link_item(IPATCH_SF2_ZONE(item), NULL); } if(IPATCH_ITEM_CLASS(ipatch_sf2_zone_parent_class)->remove_full) { IPATCH_ITEM_CLASS(ipatch_sf2_zone_parent_class)->remove_full(item, full); } } /** * ipatch_sf2_zone_first: (skip) * @iter: Patch item iterator containing #IpatchSF2Zone items * * Gets the first item in a zone iterator. A convenience * wrapper for ipatch_iter_first(). * * Returns: The first zone in @iter or %NULL if empty. */ IpatchSF2Zone * ipatch_sf2_zone_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_SF2_ZONE(obj)); } else { return (NULL); } } /** * ipatch_sf2_zone_next: (skip) * @iter: Patch item iterator containing #IpatchSF2Zone items * * Gets the next item in a zone iterator. A convenience wrapper * for ipatch_iter_next(). * * Returns: The next zone in @iter or %NULL if at the end of * the list. */ IpatchSF2Zone * ipatch_sf2_zone_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_SF2_ZONE(obj)); } else { return (NULL); } } /** * ipatch_sf2_zone_set_link_item: * @zone: Zone to set zone item of * @item: (nullable): New item for zone to use * * Sets the referenced item of a zone (a #IpatchSF2Inst for preset zones, * #IpatchSF2Sample for instrument zones). The type specific item set routines * for each zone type may be preferred, as this one doesn't do strict type * checking. */ void ipatch_sf2_zone_set_link_item(IpatchSF2Zone *zone, IpatchItem *item) { GValue oldval = { 0 }, newval = { 0 }; IpatchItem *olditem; if(!ipatch_sf2_zone_set_link_item_no_notify(zone, item, &olditem)) { return; } g_value_init(&oldval, G_TYPE_OBJECT); g_value_take_object(&oldval, olditem); /* !! take over reference */ g_value_init(&newval, G_TYPE_OBJECT); g_value_set_object(&newval, (GObject *)item); ipatch_item_prop_notify_by_name((IpatchItem *)zone, "link-item", &newval, &oldval); g_value_unset(&oldval); g_value_unset(&newval); } /** * ipatch_sf2_zone_set_link_item_no_notify: * @zone: Zone to set zone item of * @item: (nullable): New item for zone to use * @olditem: (out) (optional) (transfer full): Pointer to store old item pointer or %NULL to ignore. * Caller owns reference if specified. * * Like ipatch_sf2_zone_set_link_item() but performs no property or item * change notifications for "link-item" property (shouldn't normally be used outside of derived types), * and the old value can be retrieved with the @olditem parameter. * * Returns: TRUE if property was changed, FALSE otherwise (invalid inputs) */ gboolean ipatch_sf2_zone_set_link_item_no_notify(IpatchSF2Zone *zone, IpatchItem *item, IpatchItem **olditem) { IpatchItem *oldie; GValue old_title = { 0 }, new_title = { 0 }; if(olditem) { *olditem = NULL; /* in case of failure */ } g_return_val_if_fail(IPATCH_IS_SF2_ZONE(zone), FALSE); g_return_val_if_fail(!item || IPATCH_IS_ITEM(item), FALSE); ipatch_item_get_property_fast(IPATCH_ITEM(zone), ipatch_item_pspec_title, &old_title); // ++ alloc value if(item) { g_object_ref(item); /* ref for zone */ } IPATCH_ITEM_WLOCK(zone); oldie = zone->item; zone->item = item; IPATCH_ITEM_WUNLOCK(zone); /* remove "title" notify on old item */ if(oldie) ipatch_item_prop_disconnect_matched(oldie, ipatch_item_pspec_title, ipatch_sf2_zone_link_item_title_notify, zone); /* add a prop notify on link-item "title" so zone can notify it's title also */ if(item) ipatch_item_prop_connect(item, ipatch_item_pspec_title, ipatch_sf2_zone_link_item_title_notify, NULL, zone); /* either caller takes reference to old item or we unref it, unref outside of lock */ if(olditem) { *olditem = oldie; } else if(oldie) { g_object_unref(oldie); } ipatch_item_get_property_fast(IPATCH_ITEM(zone), ipatch_item_pspec_title, &new_title); // ++ alloc value ipatch_item_prop_notify((IpatchItem *)zone, ipatch_item_pspec_title, &old_title, &new_title); g_value_unset(&old_title); // -- free value g_value_unset(&new_title); // -- free value return (TRUE); } /* property notify for when link-item "title" property changes */ static void ipatch_sf2_zone_link_item_title_notify(IpatchItemPropNotify *info) { /* notify that zone's title changed */ IpatchItem *zone = (IpatchItem *)(info->user_data); ipatch_item_prop_notify_by_name(zone, "title", info->new_value, info->old_value); } /** * ipatch_sf2_zone_get_link_item: * @zone: Zone to get referenced item of * * Gets the referenced item from a zone (a #IpatchSF2Inst for preset zones, * #IpatchSF2Sample for instrument zones). The type specific item set routines * for each zone type may be preferred, as this one doesn't do strict type * checking. The returned item's reference count is incremented and the caller * is responsible for unrefing it with g_object_unref(). * * Returns: (transfer full): Zone's referenced item or %NULL if global zone. Remember to * unreference the item with g_object_unref() when done with it. */ IpatchItem * ipatch_sf2_zone_get_link_item(IpatchSF2Zone *zone) { IpatchItem *item; g_return_val_if_fail(IPATCH_IS_SF2_ZONE(zone), NULL); IPATCH_ITEM_RLOCK(zone); item = zone->item; if(item) { g_object_ref(item); } IPATCH_ITEM_RUNLOCK(zone); return (item); } /** * ipatch_sf2_zone_peek_link_item: (skip) * @zone: Zone to get referenced item of * * Like ipatch_sf2_zone_get_link_item() but does not add a reference to * the returned item. This function should only be used if a reference * of the returned item is ensured or only the pointer value is of * interest. * * Returns: (transfer none): Zone's linked item. Remember that the item has NOT been referenced. */ IpatchItem * ipatch_sf2_zone_peek_link_item(IpatchSF2Zone *zone) { g_return_val_if_fail(IPATCH_IS_SF2_ZONE(zone), NULL); return (zone->item); /* atomic read */ } libinstpatch-1.1.6/libinstpatch/IpatchSF2Zone.h000066400000000000000000000070251400263525300214230ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SF2_ZONE_H__ #define __IPATCH_SF2_ZONE_H__ #include #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchSF2Zone IpatchSF2Zone; typedef struct _IpatchSF2ZoneClass IpatchSF2ZoneClass; #define IPATCH_TYPE_SF2_ZONE (ipatch_sf2_zone_get_type ()) #define IPATCH_SF2_ZONE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SF2_ZONE, \ IpatchSF2Zone)) #define IPATCH_SF2_ZONE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SF2_ZONE, \ IpatchSF2ZoneClass)) #define IPATCH_IS_SF2_ZONE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SF2_ZONE)) #define IPATCH_IS_SF2_ZONE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SF2_ZONE)) #define IPATCH_SF2_ZONE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SF2_ZONE, \ IpatchSF2ZoneClass)) /* SoundFont zone item */ struct _IpatchSF2Zone { IpatchItem parent_instance; /*< private >*/ IpatchItem *item; /* referenced item */ GSList *mods; /* modulators */ IpatchSF2GenArray genarray; /* generator array */ }; struct _IpatchSF2ZoneClass { IpatchItemClass parent_class; }; /* reserve 2 flags */ #define IPATCH_SF2_ZONE_UNUSED_FLAG_SHIFT (IPATCH_ITEM_UNUSED_FLAG_SHIFT + 2) /* a macro for getting or setting a generator value directly, normally ipatch_sf2_zone_(get/set)_gen should be used instead, zone is not locked and no flags are set (should only be used on zones with exclusive access) */ #define IPATCH_SF2_ZONE_GEN_AMT(zone, genid) \ ((IpatchSF2Zone *)zone)->genarray.values[genid] /* macros for manipulating zone generator set flags directly, should not normally be used except in the case of exclusive access, not locked, etc */ #define IPATCH_SF2_ZONE_GEN_TEST_FLAG(zone, genid) \ IPATCH_SF2_GEN_ARRAY_TEST_FLAG (&((IpatchSF2Zone *)(zone))->genarray, genid) #define IPATCH_SF2_ZONE_GEN_SET_FLAG(zone, genid) \ IPATCH_SF2_GEN_ARRAY_SET_FLAG (&((IpatchSF2Zone *)(zone))->genarray, genid) #define IPATCH_SF2_ZONE_GEN_CLEAR_FLAG(zone, genid) \ IPATCH_SF2_GEN_ARRAY_CLEAR_FLAG (&((IpatchSF2Zone *)(zone))->genarray, genid) GType ipatch_sf2_zone_get_type(void); IpatchSF2Zone *ipatch_sf2_zone_first(IpatchIter *iter); IpatchSF2Zone *ipatch_sf2_zone_next(IpatchIter *iter); void ipatch_sf2_zone_set_link_item(IpatchSF2Zone *zone, IpatchItem *item); gboolean ipatch_sf2_zone_set_link_item_no_notify(IpatchSF2Zone *zone, IpatchItem *item, IpatchItem **olditem); IpatchItem *ipatch_sf2_zone_get_link_item(IpatchSF2Zone *zone); IpatchItem *ipatch_sf2_zone_peek_link_item(IpatchSF2Zone *zone); #endif libinstpatch-1.1.6/libinstpatch/IpatchSLI.c000066400000000000000000000426671400263525300206320ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSLI * @short_description: Spectralis instrument file object * @see_also: * * Object type for Spectralis format instruments. */ #include #include #include #include #include "IpatchSLI.h" #include "IpatchSLIFile.h" #include "IpatchSLIZone.h" #include "IpatchParamProp.h" #include "IpatchTypeProp.h" #include "IpatchVirtualContainer_types.h" #include "version.h" #include "ipatch_priv.h" /* properties */ enum { PROP_0, PROP_TITLE }; static void ipatch_sli_class_init(IpatchSLIClass *klass); static void ipatch_sli_init(IpatchSLI *sli); static void ipatch_sli_get_title(IpatchSLI *sli, GValue *value); static void ipatch_sli_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sli_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static const GType *ipatch_sli_container_child_types(void); static const GType *ipatch_sli_container_virtual_types(void); static gboolean ipatch_sli_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type); static void ipatch_sli_container_make_unique(IpatchContainer *container, IpatchItem *item); static void ipatch_sli_parent_file_prop_notify(IpatchItemPropNotify *info); static gpointer parent_class = NULL; static GType sli_child_types[3] = { 0 }; static GType sli_virt_types[3] = { 0 }; /* Spectralis item type creation function */ GType ipatch_sli_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchSLIClass), NULL, NULL, (GClassInitFunc)ipatch_sli_class_init, NULL, NULL, sizeof(IpatchSLI), 0, (GInstanceInitFunc)ipatch_sli_init, }; item_type = g_type_register_static(IPATCH_TYPE_BASE, "IpatchSLI", &item_info, 0); } return (item_type); } static void ipatch_sli_class_init(IpatchSLIClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->get_property = ipatch_sli_get_property; item_class->copy = ipatch_sli_item_copy; container_class->child_types = ipatch_sli_container_child_types; container_class->virtual_types = ipatch_sli_container_virtual_types; container_class->init_iter = ipatch_sli_container_init_iter; container_class->make_unique = ipatch_sli_container_make_unique; g_object_class_override_property(obj_class, PROP_TITLE, "title"); sli_child_types[0] = IPATCH_TYPE_SLI_INST; sli_child_types[1] = IPATCH_TYPE_SLI_SAMPLE; sli_virt_types[0] = IPATCH_TYPE_VIRTUAL_SLI_INST; sli_virt_types[1] = IPATCH_TYPE_VIRTUAL_SLI_SAMPLES; } static void ipatch_sli_init(IpatchSLI *sli) { ipatch_item_clear_flags(IPATCH_ITEM(sli), IPATCH_BASE_CHANGED); /* add a prop notify on file-name so sli can notify it's title also */ ipatch_item_prop_connect_by_name(IPATCH_ITEM(sli), "file-name", ipatch_sli_parent_file_prop_notify, NULL, sli); } static void ipatch_sli_get_title(IpatchSLI *sli, GValue *value) { char *filename; gchar *s; filename = ipatch_base_get_file_name(IPATCH_BASE(sli)); s = (filename ? g_path_get_basename(filename) : NULL); free(filename); if(!s || *s == G_DIR_SEPARATOR || *s == '.') { g_free(s); s = g_strdup(_(IPATCH_BASE_DEFAULT_NAME)); } g_value_take_string(value, s); } static void ipatch_sli_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSLI *sli; g_return_if_fail(IPATCH_IS_SLI(object)); sli = IPATCH_SLI(object); switch(property_id) { case PROP_TITLE: ipatch_sli_get_title(sli, value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_sli_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchSLI *src_sli, *dest_sli; IpatchItem *newitem; GHashTable *repl_samples; GSList *p; src_sli = IPATCH_SLI(src); dest_sli = IPATCH_SLI(dest); /* create item replacement hash */ repl_samples = g_hash_table_new(NULL, NULL); IPATCH_ITEM_RLOCK(src_sli); if(IPATCH_BASE(src_sli)->file) { ipatch_base_set_file(IPATCH_BASE(dest_sli), IPATCH_BASE(src_sli)->file); } p = src_sli->samples; while(p) /* duplicate samples */ { /* ++ ref new duplicate sample, !! sample list takes it over */ newitem = ipatch_item_duplicate((IpatchItem *)(p->data)); dest_sli->samples = g_slist_prepend(dest_sli->samples, newitem); ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_sli)); /* add to sample pointer replacement hash */ g_hash_table_insert(repl_samples, p->data, newitem); p = g_slist_next(p); } p = src_sli->insts; while(p) /* duplicate instruments */ { /* ++ ref new duplicate instrument, !! instrument list takes it over * duplicate instrument and replace referenced sample pointers. */ newitem = ipatch_item_duplicate_replace((IpatchItem *)(p->data), repl_samples); dest_sli->insts = g_slist_prepend(dest_sli->insts, newitem); ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_sli)); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(src_sli); dest_sli->insts = g_slist_reverse(dest_sli->insts); dest_sli->samples = g_slist_reverse(dest_sli->samples); g_hash_table_destroy(repl_samples); } static const GType * ipatch_sli_container_child_types(void) { return (sli_child_types); } static const GType * ipatch_sli_container_virtual_types(void) { return (sli_virt_types); } /* container is locked by caller */ static gboolean ipatch_sli_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type) { IpatchSLI *sli = IPATCH_SLI(container); if(g_type_is_a(type, IPATCH_TYPE_SLI_INST)) { ipatch_iter_GSList_init(iter, &sli->insts); } else if(g_type_is_a(type, IPATCH_TYPE_SLI_SAMPLE)) { ipatch_iter_GSList_init(iter, &sli->samples); } else { g_critical("Invalid child type '%s' for parent of type '%s'", g_type_name(type), g_type_name(G_OBJECT_TYPE(container))); return (FALSE); } return (TRUE); } static void ipatch_sli_container_make_unique(IpatchContainer *container, IpatchItem *item) { IpatchSLI *sli = IPATCH_SLI(container); char *name, *newname; if(!(IPATCH_IS_SLI_INST(item) || IPATCH_IS_SLI_SAMPLE(item))) { g_critical("Invalid child type '%s' for IpatchSLI object", g_type_name(G_TYPE_FROM_INSTANCE(item))); return; } IPATCH_ITEM_WLOCK(sli); g_object_get(item, "name", &name, NULL); newname = ipatch_sli_make_unique_name(sli, G_TYPE_FROM_INSTANCE(item), name, NULL); if(!name || strcmp(name, newname) != 0) { g_object_set(item, "name", newname, NULL); } IPATCH_ITEM_WUNLOCK(sli); g_free(name); g_free(newname); } /* property notify for when parent's "file-name" property changes */ static void ipatch_sli_parent_file_prop_notify(IpatchItemPropNotify *info) { IpatchItem *sli = (IpatchItem *)(info->user_data); /* notify that SLI's title has changed */ ipatch_item_prop_notify(sli, ipatch_item_pspec_title, info->new_value, info->old_value); } /** * ipatch_sli_new: * * Create a new Spectralis base object. * * Returns: New Spectralis base object with a reference count of 1. Caller * owns the reference and removing it will destroy the item. */ IpatchSLI * ipatch_sli_new(void) { return (IPATCH_SLI(g_object_new(IPATCH_TYPE_SLI, NULL))); } /** * ipatch_sli_set_file: * @sli: SLI object to set file object of * @file: File object to use with the SLI object * * Sets the file object of a SLI object. SLI files are kept open * for sample data that references the file. This function sets a * Spectralis object's authoritive file. A convenience function, as * ipatch_base_set_file() does the same thing (albeit without more specific * type casting). */ void ipatch_sli_set_file(IpatchSLI *sli, IpatchSLIFile *file) { g_return_if_fail(IPATCH_IS_SLI(sli)); g_return_if_fail(IPATCH_IS_SLI_FILE(file)); ipatch_base_set_file(IPATCH_BASE(sli), IPATCH_FILE(file)); } /** * ipatch_sli_get_file: * @sli: SLI object to get file object of * * Gets the file object of a SLI object. The returned SLI file object's * reference count has been incremented. The caller owns the reference and is * responsible for removing it with g_object_unref. * A convenience function as ipatch_base_get_file() does the same thing * (albeit without more specific type casting). * * Returns: (transfer full): The SLI file object or %NULL if @sli is not open. Remember * to unref the file object with g_object_unref() when done with it. */ IpatchSLIFile * ipatch_sli_get_file(IpatchSLI *sli) { IpatchFile *file; g_return_val_if_fail(IPATCH_IS_SLI(sli), NULL); file = ipatch_base_get_file(IPATCH_BASE(sli)); if(file) { return (IPATCH_SLI_FILE(file)); } else { return (NULL); } } /** * ipatch_sli_make_unique_name: * @sli: SLI object * @child_type: A child type of @sli to search for a unique name in * @name: (nullable): An initial name to use or %NULL * @exclude: (nullable): An item to exclude from search or %NULL * * Generates a unique name for the given @child_type in @sli. The @name * parameter is used as a base and is modified, by appending a number, to * make it unique (if necessary). The @exclude parameter is used to exclude * an existing @sli child item from the search. * * MT-Note: To ensure that an item is actually unique before being * added to a SLI object, ipatch_container_add_unique() should be * used. * * Returns: A new unique name which should be freed when finished with it. */ char * ipatch_sli_make_unique_name(IpatchSLI *sli, GType child_type, const char *name, const IpatchItem *exclude) { GSList **list, *p; char curname[IPATCH_SLI_NAME_SIZE + 1]; guint name_ofs, count = 2; g_return_val_if_fail(IPATCH_IS_SLI(sli), NULL); if(g_type_is_a(child_type, IPATCH_TYPE_SLI_INST)) { list = &sli->insts; name_ofs = G_STRUCT_OFFSET(IpatchSLIInst, name); if(!name || !*name) { name = _("New Instrument"); } } else if(g_type_is_a(child_type, IPATCH_TYPE_SLI_SAMPLE)) { list = &sli->samples; name_ofs = G_STRUCT_OFFSET(IpatchSLISample, name); if(!name || !*name) { name = _("New Sample"); } } else { g_critical("Invalid child type '%s' of parent type '%s'", g_type_name(child_type), g_type_name(G_OBJECT_TYPE(sli))); return (NULL); } g_strlcpy(curname, name, sizeof(curname)); IPATCH_ITEM_RLOCK(sli); p = *list; while(p) /* check for duplicate */ { IPATCH_ITEM_RLOCK(p->data); /* MT - Recursive LOCK */ if(p->data != exclude && strcmp(G_STRUCT_MEMBER(char *, p->data, name_ofs), curname) == 0) { /* duplicate name */ IPATCH_ITEM_RUNLOCK(p->data); ipatch_strconcat_num(name, count++, curname, sizeof(curname)); p = *list; /* start over */ continue; } IPATCH_ITEM_RUNLOCK(p->data); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(sli); return (g_strdup(curname)); } /** * ipatch_sli_find_inst: * @sli: SLI to search in * @name: Name of Instrument to find * @exclude: (nullable): An instrument to exclude from the search or %NULL * * Find an instrument by @name in an SLI object. If a matching instrument * is found, its reference count is incremented before it is returned. * The caller is responsible for removing the reference with g_object_unref() * when finished with it. * * Returns: (transfer full): The matching instrument or %NULL if not found. Remember to unref * the item when finished with it. */ IpatchSLIInst * ipatch_sli_find_inst(IpatchSLI *sli, const char *name, const IpatchSLIInst *exclude) { IpatchSLIInst *inst; GSList *p; g_return_val_if_fail(IPATCH_IS_SLI(sli), NULL); g_return_val_if_fail(name != NULL, NULL); IPATCH_ITEM_RLOCK(sli); p = sli->insts; while(p) { inst = (IpatchSLIInst *)(p->data); IPATCH_ITEM_RLOCK(inst); /* MT - Recursive LOCK */ if(inst != exclude && strcmp(inst->name, name) == 0) { g_object_ref(inst); IPATCH_ITEM_RUNLOCK(inst); IPATCH_ITEM_RUNLOCK(sli); return (inst); } IPATCH_ITEM_RUNLOCK(inst); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(sli); return (NULL); } /** * ipatch_sli_find_sample: * @sli: SLI to search in * @name: Name of sample to find * @exclude: (nullable): A sample to exclude from the search or %NULL * * Find a sample by @name in a SLI object. If a sample is found its * reference count is incremented before it is returned. The caller * is responsible for removing the reference with g_object_unref() * when finished with it. * * Returns: (transfer full): The matching sample or %NULL if not found. Remember to unref * the item when finished with it. */ IpatchSLISample * ipatch_sli_find_sample(IpatchSLI *sli, const char *name, const IpatchSLISample *exclude) { IpatchSLISample *sample; GSList *p; g_return_val_if_fail(IPATCH_IS_SLI(sli), NULL); g_return_val_if_fail(name != NULL, NULL); IPATCH_ITEM_RLOCK(sli); p = sli->samples; while(p) { sample = (IpatchSLISample *)(p->data); IPATCH_ITEM_RLOCK(sample); /* MT - Recursive LOCK */ if(p->data != exclude && strcmp(sample->name, name) == 0) { g_object_ref(sample); IPATCH_ITEM_RUNLOCK(sample); IPATCH_ITEM_RUNLOCK(sli); return (p->data); } IPATCH_ITEM_RUNLOCK(sample); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(sli); return (NULL); } /** * ipatch_sli_get_zone_references: * @sample: Sample to locate referencing zones of. * * Get list of zones referencing an IpatchSLISample. * * Returns: (transfer full): New item list containing #IpatchSLIZone objects * that refer to @sample. The returned list has a reference count of 1 which * the caller owns, unreference to free the list. */ IpatchList * ipatch_sli_get_zone_references(IpatchSLISample *sample) { IpatchList *reflist, *instlist, *zonelist; IpatchSLI *sli; IpatchSLIZone *zone; IpatchIter iter, zone_iter; IpatchItem *pitem; g_return_val_if_fail(IPATCH_IS_SLI_SAMPLE(sample), NULL); pitem = ipatch_item_get_parent(IPATCH_ITEM(sample)); g_return_val_if_fail(IPATCH_IS_SLI(pitem), NULL); sli = IPATCH_SLI(pitem); reflist = ipatch_list_new(); /* ++ ref new list */ instlist = ipatch_sli_get_insts(sli); /* ++ ref instlist */ ipatch_list_init_iter(instlist, &iter); pitem = ipatch_item_first(&iter); while(pitem) /* loop over instruments */ { zonelist = ipatch_sli_inst_get_zones(pitem); /* ++ ref new zone list */ ipatch_list_init_iter(zonelist, &zone_iter); zone = ipatch_sli_zone_first(&zone_iter); while(zone) { if(ipatch_sli_zone_peek_sample(zone) == sample) { g_object_ref(zone); /* ++ ref zone for new list */ reflist->items = g_list_prepend(reflist->items, zone); } zone = ipatch_sli_zone_next(&zone_iter); } g_object_unref(zonelist); /* -- unref zone list */ pitem = ipatch_item_next(&iter); } g_object_unref(instlist); /* -- unref instlist */ /* reverse list to preserve order since we prepended */ reflist->items = g_list_reverse(reflist->items); return (reflist); /* !! caller takes over reference */ } libinstpatch-1.1.6/libinstpatch/IpatchSLI.h000066400000000000000000000061011400263525300206160ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SLI_H__ #define __IPATCH_SLI_H__ #include #include /* forward type declarations */ typedef struct _IpatchSLI IpatchSLI; typedef struct _IpatchSLIClass IpatchSLIClass; #include #include #include #include #include #define IPATCH_TYPE_SLI (ipatch_sli_get_type ()) #define IPATCH_SLI(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SLI, IpatchSLI)) #define IPATCH_SLI_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SLI, IpatchSLIClass)) #define IPATCH_IS_SLI(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SLI)) #define IPATCH_IS_SLI_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SLI)) #define IPATCH_SLI_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SLI, IpatchSLIClass)) /* Spectralis object */ struct _IpatchSLI { /*< public >*/ IpatchBase parent_instance; GSList *insts; /* list of #IpatchSLIInst objects */ GSList *samples; /* list of #IpatchSLISample objects */ }; /* Spectralis class */ struct _IpatchSLIClass { IpatchBaseClass parent_class; }; GType ipatch_sli_get_type(void); IpatchSLI *ipatch_sli_new(void); #define ipatch_sli_get_insts(sli) \ ipatch_container_get_children (IPATCH_CONTAINER (sli), \ IPATCH_TYPE_SLI_INST) #define ipatch_sli_get_samples(sli) \ ipatch_container_get_children (IPATCH_CONTAINER (sli), \ IPATCH_TYPE_SLI_SAMPLE) void ipatch_sli_set_file(IpatchSLI *sli, IpatchSLIFile *file); IpatchSLIFile *ipatch_sli_get_file(IpatchSLI *sli); char *ipatch_sli_make_unique_name(IpatchSLI *sli, GType child_type, const char *name, const IpatchItem *exclude); IpatchSLIInst *ipatch_sli_find_inst(IpatchSLI *sli, const char *name, const IpatchSLIInst *exclude); IpatchSLISample *ipatch_sli_find_sample(IpatchSLI *sli, const char *name, const IpatchSLISample *exclude); IpatchList *ipatch_sli_get_zone_references(IpatchSLISample *sample); #endif libinstpatch-1.1.6/libinstpatch/IpatchSLIFile.c000066400000000000000000000057441400263525300214250ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSLIFile * @short_description: Spectralis file object * @see_also: * * A #IpatchFile object type for Spectralis instrument and instrument collection * files. */ #include #include #include #include "IpatchSLIFile.h" #include "IpatchSLIFile_priv.h" #include "ipatch_priv.h" static gboolean ipatch_sli_file_identify(IpatchFile *file, IpatchFileHandle *handle, GError **err); G_DEFINE_TYPE(IpatchSLIFile, ipatch_sli_file, IPATCH_TYPE_FILE) /* Spectralis file class init function */ static void ipatch_sli_file_class_init(IpatchSLIFileClass *klass) { IpatchFileClass *file_class = IPATCH_FILE_CLASS(klass); file_class->identify = ipatch_sli_file_identify; } static void ipatch_sli_file_init(IpatchSLIFile *file) { } /* Spectralis file identification method */ static gboolean ipatch_sli_file_identify(IpatchFile *file, IpatchFileHandle *handle, GError **err) { guint32 buf[3]; char *filename; int len; if(handle) /* Test content */ { if(!ipatch_file_read(handle, buf, 12, err)) { return (FALSE); } if(buf[0] == IPATCH_SLI_FOURCC_SIFI && buf[2] == 0x100) { return (TRUE); } } /* Test file name extension */ else if((filename = ipatch_file_get_name(file))) /* ++ alloc file name */ { len = strlen(filename); if(len >= 4 && (g_ascii_strcasecmp(filename + len - 4, ".slc") == 0 || g_ascii_strcasecmp(filename + len - 4, ".sli") == 0)) { g_free(filename); /* -- free file name */ return (TRUE); } g_free(filename); /* -- free file name */ } return (FALSE); } /** * ipatch_sli_file_new: * * Create a new Spectralis file object. * * Returns: New Spectralis file object (derived from IpatchFile) with a * reference count of 1. Caller owns the reference and removing it will * destroy the item. */ IpatchSLIFile * ipatch_sli_file_new(void) { return (IPATCH_SLI_FILE(g_object_new(IPATCH_TYPE_SLI_FILE, NULL))); } libinstpatch-1.1.6/libinstpatch/IpatchSLIFile.h000066400000000000000000000043371400263525300214270ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SLI_FILE_H__ #define __IPATCH_SLI_FILE_H__ #include #include #include #include /* forward type declarations */ typedef struct _IpatchSLIFile IpatchSLIFile; typedef struct _IpatchSLIFileClass IpatchSLIFileClass; #define IPATCH_TYPE_SLI_FILE (ipatch_sli_file_get_type ()) #define IPATCH_SLI_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SLI_FILE, IpatchSLIFile)) #define IPATCH_SLI_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SLI_FILE, \ IpatchSLIFileClass)) #define IPATCH_IS_SLI_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SLI_FILE)) #define IPATCH_IS_SLI_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SLI_FILE)) #define IPATCH_SLI_FILE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SLI_FILE, \ IpatchSLIFileClass)) /** * IPATCH_SLI_NAME_SIZE: (skip) */ #define IPATCH_SLI_NAME_SIZE 24 /* name string size (Inst/Sample) */ /* Spectralis file object (derived from IpatchFile) */ struct _IpatchSLIFile { IpatchFile parent_instance; }; /* Spectralis file class (derived from IpatchFile) */ struct _IpatchSLIFileClass { IpatchFileClass parent_class; }; GType ipatch_sli_file_get_type(void); IpatchSLIFile *ipatch_sli_file_new(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchSLIFile_priv.h000066400000000000000000000135751400263525300224730ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SLI_FILE_PRIV_H__ #define __IPATCH_SLI_FILE_PRIV_H__ #include /* forward type declarations */ typedef struct _IpatchSLISiFi IpatchSLISiFi; typedef struct _IpatchSLISiIg IpatchSLISiIg; typedef struct _IpatchSLIInstHeader IpatchSLIInstHeader; typedef struct _IpatchSLIZoneParams IpatchSLIZoneParams; typedef struct _IpatchSLIModParams IpatchSLIModParams; typedef struct _IpatchSLIZoneHeader IpatchSLIZoneHeader; typedef struct _IpatchSLISampleHeader IpatchSLISampleHeader; typedef struct _IpatchSLISiDp IpatchSLISiDp; /* Spectralis file header */ struct _IpatchSLISiFi { guint32 ckid; /* chunk id 'SiFi' */ guint32 cklen; /* chunk (whole file - 8) length */ guint16 spechdr; /* version? always 0x100 */ guint16 unused; /* 0 */ guint16 ignum; /* number of instrument groups */ guint16 igstart_offs; /* offset in file for start of inst. groups */ }; /* Spectralis instrument group header */ struct _IpatchSLISiIg { guint32 ckid; /* chunk id 'SiIg' */ guint32 cklen; /* number of bytes in chunk */ guint16 spechdr; /* version? always 0x100 */ guint16 unused1; /* 0 */ guint16 inst_offs; /* offset in chunk for start of inst. headers */ guint16 instnum; /* number of instruments in group */ guint16 zones_offs; /* offset to zone headers */ guint16 allzones_num; /* total number of zones in group */ guint16 smphdr_offs; /* offset to sample headers */ guint16 maxzones_num; /* largest number of zones in one inst */ guint16 smpdata_offs; /* offset to start of sample data */ guint16 unused2; /* 0 */ }; /* Spectralis instrument header */ struct _IpatchSLIInstHeader { char name[24]; /* name of instrument */ guint32 sound_id; /* unique? id of the instrument */ guint32 unused1; /* 0 */ guint16 category; /* category code for sub and main category */ guint16 unused2; /* 0 */ guint16 zone_idx; /* index of first zone header for this inst. */ guint16 zones_num; /* number of zones for this inst. */ }; /* Spectralis zone params */ struct _IpatchSLIZoneParams { guint8 keyrange_low; guint8 keyrange_high; guint8 velrange_low; guint8 velrange_high; guint32 start_offs1; guint32 start_offs2; guint32 unknown1; guint32 unknown2; gint8 coarse_tune1; gint8 fine_tune1; guint8 sample_modes; gint8 root_note; guint16 scale_tuning; gint8 coarse_tune2; gint8 fine_tune2; }; /* Spectralis mod params */ struct _IpatchSLIModParams { gint16 modLfoToPitch; gint16 vibLfoToPitch; gint16 modEnvToPitch; guint16 initialFilterFc; guint16 initialFilterQ; gint16 modLfoToFilterFc; gint16 modEnvToFilterFc; gint16 modLfoToVolume; gint16 freqModLfo; gint16 freqVibLfo; guint16 sustainModEnv; gint16 keynumToModEnvHold; gint16 keynumToModEnvDecay; guint16 sustainVolEnv; gint16 keynumToVolEnvHold; gint16 keynumToVolEnvDecay; gint8 pan; gint8 delayModLfo; gint8 delayVibLfo; gint8 attackModEnv; gint8 holdModEnv; gint8 decayModEnv; gint8 releaseModEnv; gint8 attackVolEnv; gint8 holdVolEnv; gint8 decayVolEnv; gint8 releaseVolEnv; guint8 initialAttenuation; }; /* Spectralis Zone header */ struct _IpatchSLIZoneHeader { IpatchSLIZoneParams zone_params; IpatchSLIModParams mod_params; guint16 sample_idx; guint16 unused; }; /* Spectralis file sample header */ struct _IpatchSLISampleHeader { char name[24]; /* sample name */ guint32 start; /* offset to start of sample */ guint32 end; /* offset to end of sample */ guint32 loop_start; /* offset to start of loop */ guint32 loop_end; /* offset to end of loop */ gint8 fine_tune; /* pitch correction in cents */ guint8 root_note; /* root midi note number */ guint8 channels; /* number of channels */ guint8 bits_per_sample; /* number of bits per sample */ guint32 sample_rate; /* sample rate recorded at */ }; /* Spectralis Instrument End header */ struct _IpatchSLISiDp { guint32 ckid; /* chunk id 'SiDp' */ guint32 cklen; /* number of bytes in chunk */ guint16 spechdr; /* version? always 0x100 */ guint16 unused; /* 0 */ }; /* Spectralis chunk FOURCC guint32 integers */ #define IPATCH_SLI_FOURCC_SIFI IPATCH_FOURCC ('S','i','F','i') #define IPATCH_SLI_FOURCC_SIIG IPATCH_FOURCC ('S','i','I','g') #define IPATCH_SLI_FOURCC_SIDP IPATCH_FOURCC ('S','i','D','p') #define IPATCH_SLI_SPECHDR_VAL 0x100 /* Spectralis file chunk sizes */ #define IPATCH_SLI_SIFI_SIZE 8 /* file info header size (w/o RIFF header) */ #define IPATCH_SLI_SIIG_SIZE 28 /* inst. group header size */ #define IPATCH_SLI_INST_SIZE 40 /* instrument header size */ #define IPATCH_SLI_ZONE_SIZE 76 /* zone params header size */ #define IPATCH_SLI_SMPL_SIZE 48 /* sample data header size */ #define IPATCH_SLI_SIDP_SIZE 12 /* instrument terminator size */ #define IPATCH_SLI_HEAD_SIZE 64*1024 /* maximal size of headers */ #endif libinstpatch-1.1.6/libinstpatch/IpatchSLIInst.c000066400000000000000000000326751400263525300214660ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSLIInst * @short_description: Spectralis instrument object * @see_also: #IpatchSLI, #IpatchSLIZone * * Spectralis instruments are children of #IpatchSLI objects and are referenced * by #IpatchSLIZone objects. */ #include #include #include #include #include "IpatchSLIInst.h" #include "IpatchSLIZone.h" #include "IpatchSLI.h" #include "IpatchSLIFile.h" #include "IpatchParamProp.h" #include "IpatchTypeProp.h" #include "ipatch_priv.h" /* properties */ enum { PROP_0, PROP_NAME, PROP_SOUND_ID, PROP_CATEGORY }; static void ipatch_sli_inst_class_init(IpatchSLIInstClass *klass); static void ipatch_sli_inst_init(IpatchSLIInst *inst); static void ipatch_sli_inst_finalize(GObject *gobject); static void ipatch_sli_inst_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sli_inst_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sli_inst_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static const GType *ipatch_sli_inst_container_child_types(void); static gboolean ipatch_sli_inst_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type); static void ipatch_sli_inst_real_set_name(IpatchSLIInst *inst, const char *name, gboolean name_notify); static gpointer parent_class = NULL; static GType inst_child_types[2] = { 0 }; static GParamSpec *name_pspec; GType ipatch_sli_inst_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchSLIInstClass), NULL, NULL, (GClassInitFunc)ipatch_sli_inst_class_init, NULL, NULL, sizeof(IpatchSLIInst), 0, (GInstanceInitFunc)ipatch_sli_inst_init, }; item_type = g_type_register_static(IPATCH_TYPE_CONTAINER, "IpatchSLIInst", &item_info, 0); } return (item_type); } static void ipatch_sli_inst_class_init(IpatchSLIInstClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass); parent_class = g_type_class_peek_parent(klass); obj_class->finalize = ipatch_sli_inst_finalize; obj_class->get_property = ipatch_sli_inst_get_property; /* we use the IpatchItem item_set_property method */ item_class->item_set_property = ipatch_sli_inst_set_property; item_class->copy = ipatch_sli_inst_item_copy; container_class->child_types = ipatch_sli_inst_container_child_types; container_class->init_iter = ipatch_sli_inst_container_init_iter; g_object_class_override_property(obj_class, PROP_NAME, "title"); name_pspec = ipatch_param_set(g_param_spec_string("name", "Name", "Name", NULL, G_PARAM_READWRITE | IPATCH_PARAM_UNIQUE), "string-max-length", IPATCH_SLI_NAME_SIZE, /* max string length */ NULL); g_object_class_install_property(obj_class, PROP_NAME, name_pspec); g_object_class_install_property(obj_class, PROP_SOUND_ID, g_param_spec_uint("sound-id", "SoundID", "SoundID", 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_CATEGORY, g_param_spec_uint("category", "Category", "Category", 0, G_MAXUINT, 0, G_PARAM_READWRITE)); inst_child_types[0] = IPATCH_TYPE_SLI_ZONE; } static void ipatch_sli_inst_init(IpatchSLIInst *inst) { inst->name = NULL; inst->zones = NULL; inst->sound_id = 0; inst->category = 0x4040; } static void ipatch_sli_inst_finalize(GObject *gobject) { IpatchSLIInst *inst = IPATCH_SLI_INST(gobject); IPATCH_ITEM_WLOCK(inst); g_free(inst->name); inst->name = NULL; IPATCH_ITEM_WUNLOCK(inst); if(G_OBJECT_CLASS(parent_class)->finalize) { G_OBJECT_CLASS(parent_class)->finalize(gobject); } } static void ipatch_sli_inst_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSLIInst *inst = IPATCH_SLI_INST(object); switch(property_id) { case PROP_NAME: ipatch_sli_inst_real_set_name(inst, g_value_get_string(value), FALSE); break; case PROP_SOUND_ID: IPATCH_ITEM_WLOCK(inst); inst->sound_id = g_value_get_uint(value); IPATCH_ITEM_WUNLOCK(inst); break; case PROP_CATEGORY: IPATCH_ITEM_WLOCK(inst); inst->category = g_value_get_uint(value); IPATCH_ITEM_WUNLOCK(inst); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_sli_inst_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSLIInst *inst = IPATCH_SLI_INST(object); switch(property_id) { case PROP_NAME: g_value_take_string(value, ipatch_sli_inst_get_name(inst)); break; case PROP_SOUND_ID: g_value_set_uint(value, inst->sound_id); break; case PROP_CATEGORY: g_value_set_uint(value, inst->category); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_sli_inst_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchSLIInst *src_inst, *dest_inst; IpatchItem *zitem; GSList *p; src_inst = IPATCH_SLI_INST(src); dest_inst = IPATCH_SLI_INST(dest); IPATCH_ITEM_RLOCK(src_inst); dest_inst->name = g_strdup(src_inst->name); dest_inst->sound_id = src_inst->sound_id; dest_inst->category = src_inst->category; p = src_inst->zones; while(p) /* duplicate zones */ { /* ++ ref new instrument zone, !! zone list takes it over */ zitem = ipatch_item_duplicate_link_func(IPATCH_ITEM(p->data), link_func, user_data); dest_inst->zones = g_slist_prepend(dest_inst->zones, zitem); ipatch_item_set_parent(zitem, IPATCH_ITEM(dest_inst)); p = g_slist_next(p); } IPATCH_ITEM_RUNLOCK(src_inst); dest_inst->zones = g_slist_reverse(dest_inst->zones); } static const GType * ipatch_sli_inst_container_child_types(void) { return (inst_child_types); } /* container is locked by caller */ static gboolean ipatch_sli_inst_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type) { IpatchSLIInst *inst = IPATCH_SLI_INST(container); if(!g_type_is_a(type, IPATCH_TYPE_SLI_ZONE)) { g_critical("Invalid child type '%s' for parent of type '%s'", g_type_name(type), g_type_name(G_OBJECT_TYPE(container))); return (FALSE); } ipatch_iter_GSList_init(iter, &inst->zones); return (TRUE); } /** * ipatch_sli_inst_new: * * Create a new Spectralis instrument object. * * Returns: New Spectralis instrument with a reference count of 1. Caller * owns the reference and removing it will destroy the item, unless another * reference is added (if its parented for example). */ IpatchSLIInst * ipatch_sli_inst_new(void) { return (IPATCH_SLI_INST(g_object_new(IPATCH_TYPE_SLI_INST, NULL))); } /** * ipatch_sli_inst_first: (skip) * @iter: Patch item iterator containing #IpatchSLIInst items * * Gets the first item in an instrument iterator. A convenience wrapper for * ipatch_iter_first(). * * Returns: The first instrument in @iter or %NULL if empty. */ IpatchSLIInst * ipatch_sli_inst_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_SLI_INST(obj)); } else { return (NULL); } } /** * ipatch_sli_inst_next: (skip) * @iter: Patch item iterator containing #IpatchSLIInst items * * Gets the next item in an instrument iterator. A convenience wrapper for * ipatch_iter_next(). * * Returns: The next instrument in @iter or %NULL if at the end of the list. */ IpatchSLIInst * ipatch_sli_inst_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_SLI_INST(obj)); } else { return (NULL); } } /** * ipatch_sli_inst_new_zone: * @inst: Spectralis instrument * @sample: Referenced sample for new zone * * A convenience function for quickly creating a new instrument zone, adding it * to @inst and setting the zone's referenced sample to @sample. */ void ipatch_sli_inst_new_zone(IpatchSLIInst *inst, IpatchSLISample *sample) { IpatchSLIZone *zone; g_return_if_fail(IPATCH_IS_SLI_INST(inst)); g_return_if_fail(IPATCH_IS_SLI_SAMPLE(sample)); zone = ipatch_sli_zone_new(); /* ++ ref new zone */ ipatch_sli_zone_set_sample(zone, sample); ipatch_container_append(IPATCH_CONTAINER(inst), IPATCH_ITEM(zone)); g_object_unref(zone); /* -- unref zone */ } /** * ipatch_sli_inst_set_name: * @inst: Instrument to set name of * @name: (nullable): Value to set name to * * Sets the name of a Spectralis instrument. */ void ipatch_sli_inst_set_name(IpatchSLIInst *inst, const char *name) { g_return_if_fail(IPATCH_IS_SLI_INST(inst)); ipatch_sli_inst_real_set_name(inst, name, TRUE); } /* the real inst name set routine */ static void ipatch_sli_inst_real_set_name(IpatchSLIInst *inst, const char *name, gboolean name_notify) { GValue oldval = { 0 }, newval = { 0 }; char *newname, *oldname; newname = g_strdup(name); IPATCH_ITEM_WLOCK(inst); oldname = inst->name; inst->name = newname; IPATCH_ITEM_WUNLOCK(inst); g_value_init(&oldval, G_TYPE_STRING); g_value_take_string(&oldval, oldname); g_value_init(&newval, G_TYPE_STRING); g_value_set_static_string(&newval, name); if(name_notify) { ipatch_item_prop_notify((IpatchItem *)inst, name_pspec, &newval, &oldval); } ipatch_item_prop_notify((IpatchItem *)inst, ipatch_item_pspec_title, &newval, &oldval); g_value_unset(&oldval); g_value_unset(&newval); } /** * ipatch_sli_inst_get_name: * @inst: Instrument to get name of * * Gets the name of a Spectralis instrument. * * Returns: Name of instrument or %NULL if not set. String value should be * freed when finished with it. */ char * ipatch_sli_inst_get_name(IpatchSLIInst *inst) { char *name = NULL; g_return_val_if_fail(IPATCH_IS_SLI_INST(inst), NULL); IPATCH_ITEM_RLOCK(inst); if(inst->name) { name = g_strdup(inst->name); } IPATCH_ITEM_RUNLOCK(inst); return (name); } /** * ipatch_sli_inst_get_category_as_path: * @inst: Instrument to get category of * * Gets the category of a Spectralis instrument as a string of colon separated * indexes into ipatch_sli_inst_cat_map[]. * * Returns: Category of instrument or %NULL if not set. String value should be * g_freed when finished with it. */ char * ipatch_sli_inst_get_category_as_path(IpatchSLIInst *inst) { guint cat, i; GString *catstr; const IpatchSLIInstCatMapEntry *catmap = ipatch_sli_inst_cat_map; g_return_val_if_fail(IPATCH_IS_SLI_INST(inst), NULL); g_return_val_if_fail(inst->category != 0, NULL); catstr = g_string_sized_new(6); /* ++ new str */ for(cat = GUINT16_SWAP_LE_BE(inst->category); cat; cat >>= 8) { if(cat == 0x40) /* Other subcats are treated as no subcat for UI */ { break; } if(catstr->str[0] != '\0') { g_string_append_c(catstr, ':'); } for(i = 0; catmap[i].code != '@'; i++) if((cat & 0xff) == catmap[i].code) { break; /* from inner loop */ } g_string_append_printf(catstr, "%d", i); if(catmap[i].submap == NULL) { break; } else { catmap = catmap[i].submap; } } return g_string_free(catstr, FALSE); /* !! ref to str passed to caller */ } libinstpatch-1.1.6/libinstpatch/IpatchSLIInst.h000066400000000000000000000137771400263525300214750ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SLI_INST_H__ #define __IPATCH_SLI_INST_H__ #include #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchSLIInst IpatchSLIInst; typedef struct _IpatchSLIInstClass IpatchSLIInstClass; typedef struct _IpatchSLIInstCatMapEntry IpatchSLIInstCatMapEntry; #define IPATCH_TYPE_SLI_INST (ipatch_sli_inst_get_type ()) #define IPATCH_SLI_INST(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SLI_INST, \ IpatchSLIInst)) #define IPATCH_SLI_INST_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SLI_INST, \ IpatchSLIInstClass)) #define IPATCH_IS_SLI_INST(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SLI_INST)) #define IPATCH_IS_SLI_INST_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SLI_INST)) #define IPATCH_SLI_INST_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SLI_INST, \ IpatchSLIInstClass)) /* Index constants for category strings array */ enum { IPATCH_SLI_INST_CAT_80S = 0, IPATCH_SLI_INST_CAT_A_SYNTH, IPATCH_SLI_INST_CAT_ACID, IPATCH_SLI_INST_CAT_ATTACK, IPATCH_SLI_INST_CAT_BASS, IPATCH_SLI_INST_CAT_BELL, IPATCH_SLI_INST_CAT_BIG_BEAT, IPATCH_SLI_INST_CAT_BLOCK, IPATCH_SLI_INST_CAT_BONGO, IPATCH_SLI_INST_CAT_BRASS, IPATCH_SLI_INST_CAT_CHIME, IPATCH_SLI_INST_CAT_CHINA, IPATCH_SLI_INST_CAT_CLAP, IPATCH_SLI_INST_CAT_CLAVE, IPATCH_SLI_INST_CAT_CONGA, IPATCH_SLI_INST_CAT_CRASH, IPATCH_SLI_INST_CAT_CUICA, IPATCH_SLI_INST_CAT_CYMBAL, IPATCH_SLI_INST_CAT_D_SYNTH, IPATCH_SLI_INST_CAT_DISCO, IPATCH_SLI_INST_CAT_DRUM_LOOP, IPATCH_SLI_INST_CAT_EFFECTS, IPATCH_SLI_INST_CAT_ELECTRO, IPATCH_SLI_INST_CAT_ETHNIC, IPATCH_SLI_INST_CAT_EXT_IN, IPATCH_SLI_INST_CAT_FB_LOOP, IPATCH_SLI_INST_CAT_FX_LOOP, IPATCH_SLI_INST_CAT_FUNK, IPATCH_SLI_INST_CAT_GONG, IPATCH_SLI_INST_CAT_GUIRO, IPATCH_SLI_INST_CAT_HIHAT, IPATCH_SLI_INST_CAT_HIPHOP, IPATCH_SLI_INST_CAT_HOUSE, IPATCH_SLI_INST_CAT_HUMAN, IPATCH_SLI_INST_CAT_INDUSTRY, IPATCH_SLI_INST_CAT_JAZZ, IPATCH_SLI_INST_CAT_KICK, IPATCH_SLI_INST_CAT_LEAD, IPATCH_SLI_INST_CAT_MARCH, IPATCH_SLI_INST_CAT_MARIMBA, IPATCH_SLI_INST_CAT_MULTI, IPATCH_SLI_INST_CAT_NATURAL, IPATCH_SLI_INST_CAT_OLDIE, IPATCH_SLI_INST_CAT_ORGAN, IPATCH_SLI_INST_CAT_OTHER, IPATCH_SLI_INST_CAT_PAD, IPATCH_SLI_INST_CAT_PERC_LOOP, IPATCH_SLI_INST_CAT_PERCUSSION, IPATCH_SLI_INST_CAT_PERCUSSIVE, IPATCH_SLI_INST_CAT_PIANO, IPATCH_SLI_INST_CAT_PLUG, IPATCH_SLI_INST_CAT_POP, IPATCH_SLI_INST_CAT_RELEASE, IPATCH_SLI_INST_CAT_RIDE, IPATCH_SLI_INST_CAT_ROCK, IPATCH_SLI_INST_CAT_SCRATCH, IPATCH_SLI_INST_CAT_SEQUENCER, IPATCH_SLI_INST_CAT_SHAKER, IPATCH_SLI_INST_CAT_SNARE, IPATCH_SLI_INST_CAT_SPLASH, IPATCH_SLI_INST_CAT_STRING, IPATCH_SLI_INST_CAT_SYNTH_BASS, IPATCH_SLI_INST_CAT_TR_ALIKE, IPATCH_SLI_INST_CAT_TECHNO, IPATCH_SLI_INST_CAT_TEXTURE, IPATCH_SLI_INST_CAT_TIMBALE, IPATCH_SLI_INST_CAT_TOM, IPATCH_SLI_INST_CAT_TONAL_LOOP, IPATCH_SLI_INST_CAT_TRIANGLE, IPATCH_SLI_INST_CAT_VOICE, IPATCH_SLI_INST_CAT_WHISTLE, IPATCH_SLI_INST_CAT_WIND, IPATCH_SLI_INST_CAT_WORLD }; struct _IpatchSLIInstCatMapEntry { char code; /* cat code */ guint name_idx; /* cat string index */ const IpatchSLIInstCatMapEntry *submap; /* poniter to subcat map array */ }; /* Defined in IpatchSLIInst_CatMaps.c */ extern const char *ipatch_sli_inst_cat_strings[]; /* getter fonction that returns pointer on ipatch_sli_inst_cat_strings table */ extern const gchar **ipatch_sli_inst_get_cat_strings(void); extern const IpatchSLIInstCatMapEntry ipatch_sli_inst_cat_map[]; /* getter fonction that returns pointer on ipatch_sli_inst_cat_map table */ extern const IpatchSLIInstCatMapEntry * ipatch_sli_inst_get_cat_map(void); /* SoundFont instrument item */ struct _IpatchSLIInst { IpatchContainer parent_instance; char *name; /* name of inst */ GSList *zones; /* list of inst zones */ guint sound_id; /* instrument identifier */ guint category; /* category code for grouping */ }; struct _IpatchSLIInstClass { IpatchContainerClass parent_class; }; GType ipatch_sli_inst_get_type(void); IpatchSLIInst *ipatch_sli_inst_new(void); #define ipatch_sli_inst_zones_count(inst) \ ipatch_container_count (IPATCH_CONTAINER (inst), \ IPATCH_TYPE_SLI_ZONE) #define ipatch_sli_inst_get_zones(inst) \ ipatch_container_get_children (IPATCH_CONTAINER (inst), \ IPATCH_TYPE_SLI_ZONE) IpatchSLIInst *ipatch_sli_inst_first(IpatchIter *iter); IpatchSLIInst *ipatch_sli_inst_next(IpatchIter *iter); void ipatch_sli_inst_new_zone(IpatchSLIInst *inst, IpatchSLISample *sample); void ipatch_sli_inst_set_name(IpatchSLIInst *inst, const char *name); char *ipatch_sli_inst_get_name(IpatchSLIInst *inst); char *ipatch_sli_inst_get_category_as_path(IpatchSLIInst *inst); #endif libinstpatch-1.1.6/libinstpatch/IpatchSLIInst_CatMaps.c000066400000000000000000000200011400263525300230520ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #include "IpatchSLIInst.h" #include "ipatch_priv.h" /* Category strings */ const char *ipatch_sli_inst_cat_strings[] = { "80s", "A-Synth", "Acid", "Attack", "Bass", "Bell", "Big Beat", "Block", "Bongo", "Brass", "Chime", "China", "Clap", "Clave", "Conga", "Crash", "Cuica", "Cymbal", "D-Synth", "Disco", "Drum-Loop", "Effects", "Electro", "Ethnic", "Ext-In", "FB-Loop", "FX-Loop", "Funk", "Gong", "Guiro", "HiHat", "HipHop", "House", "Human", "Industry", "Jazz", "Kick", "Lead", "March", "Marimba", "Multi", "Natural", "Oldie", "Organ", "Other", "Pad", "Perc-Loop", "Percussion", "Percussive", "Piano", "Plug", "Pop", "Release", "Ride", "Rock", "Scratch", "Sequencer", "Shaker", "Snare", "Splash", "String", "Synth-Bass", "TR-alike", "Techno", "Texture", "Timbale", "Tom", "Tonal-Loop", "Triangle", "Voice", "Whistle", "Wind", "World" }; static const IpatchSLIInstCatMapEntry ipatch_sli_inst_subcat_map_default[] = { { 'T', IPATCH_SLI_INST_CAT_TR_ALIKE, NULL }, { 'B', IPATCH_SLI_INST_CAT_BIG_BEAT, NULL }, { 'H', IPATCH_SLI_INST_CAT_HIPHOP, NULL }, { 'E', IPATCH_SLI_INST_CAT_ELECTRO, NULL }, { 'A', IPATCH_SLI_INST_CAT_ACID, NULL }, { 'C', IPATCH_SLI_INST_CAT_TECHNO, NULL }, { 'U', IPATCH_SLI_INST_CAT_HOUSE, NULL }, { 'F', IPATCH_SLI_INST_CAT_FUNK, NULL }, { 'D', IPATCH_SLI_INST_CAT_DISCO, NULL }, { 'P', IPATCH_SLI_INST_CAT_POP, NULL }, { '8', IPATCH_SLI_INST_CAT_80S, NULL }, { 'N', IPATCH_SLI_INST_CAT_NATURAL, NULL }, { 'R', IPATCH_SLI_INST_CAT_ROCK, NULL }, { 'J', IPATCH_SLI_INST_CAT_JAZZ, NULL }, { 'O', IPATCH_SLI_INST_CAT_OLDIE, NULL }, { 'W', IPATCH_SLI_INST_CAT_WORLD, NULL }, { 'X', IPATCH_SLI_INST_CAT_EFFECTS, NULL }, { '@', IPATCH_SLI_INST_CAT_OTHER, NULL } }; static const IpatchSLIInstCatMapEntry ipatch_sli_inst_subcat_map_cymbal[] = { { 'H', IPATCH_SLI_INST_CAT_CHINA, NULL }, { 'R', IPATCH_SLI_INST_CAT_RIDE, NULL }, { 'G', IPATCH_SLI_INST_CAT_GONG, NULL }, { 'E', IPATCH_SLI_INST_CAT_ETHNIC, NULL }, { 'C', IPATCH_SLI_INST_CAT_CRASH, NULL }, { 'S', IPATCH_SLI_INST_CAT_SPLASH, NULL }, { 'M', IPATCH_SLI_INST_CAT_MARCH, NULL }, { 'X', IPATCH_SLI_INST_CAT_EFFECTS, NULL }, { '@', IPATCH_SLI_INST_CAT_OTHER, NULL } }; static const IpatchSLIInstCatMapEntry ipatch_sli_inst_subcat_map_percussion[] = { { 'L', IPATCH_SLI_INST_CAT_BELL, NULL }, { 'K', IPATCH_SLI_INST_CAT_BLOCK, NULL }, { 'B', IPATCH_SLI_INST_CAT_BONGO, NULL }, { 'J', IPATCH_SLI_INST_CAT_CHIME, NULL }, { 'C', IPATCH_SLI_INST_CAT_CLAP, NULL }, { 'V', IPATCH_SLI_INST_CAT_CLAVE, NULL }, { 'O', IPATCH_SLI_INST_CAT_CONGA, NULL }, { 'I', IPATCH_SLI_INST_CAT_CUICA, NULL }, { 'X', IPATCH_SLI_INST_CAT_EFFECTS, NULL }, { 'E', IPATCH_SLI_INST_CAT_ETHNIC, NULL }, { 'G', IPATCH_SLI_INST_CAT_GUIRO, NULL }, { 'H', IPATCH_SLI_INST_CAT_HUMAN, NULL }, { 'Y', IPATCH_SLI_INST_CAT_INDUSTRY, NULL }, { 'M', IPATCH_SLI_INST_CAT_MARIMBA, NULL }, { 'R', IPATCH_SLI_INST_CAT_SCRATCH, NULL }, { 'S', IPATCH_SLI_INST_CAT_SHAKER, NULL }, { 'T', IPATCH_SLI_INST_CAT_TIMBALE, NULL }, { 'N', IPATCH_SLI_INST_CAT_TRIANGLE, NULL }, { 'W', IPATCH_SLI_INST_CAT_WHISTLE, NULL }, { '@', IPATCH_SLI_INST_CAT_OTHER, NULL } }; static const IpatchSLIInstCatMapEntry ipatch_sli_inst_subcat_map_asynth[] = { { 'S', IPATCH_SLI_INST_CAT_SYNTH_BASS, NULL }, { 'B', IPATCH_SLI_INST_CAT_BASS, NULL }, { 'Q', IPATCH_SLI_INST_CAT_SEQUENCER, NULL }, { 'U', IPATCH_SLI_INST_CAT_PLUG, NULL }, { 'A', IPATCH_SLI_INST_CAT_ATTACK, NULL }, { 'R', IPATCH_SLI_INST_CAT_RELEASE, NULL }, { 'L', IPATCH_SLI_INST_CAT_LEAD, NULL }, { 'P', IPATCH_SLI_INST_CAT_PAD, NULL }, { 'G', IPATCH_SLI_INST_CAT_STRING, NULL }, { 'F', IPATCH_SLI_INST_CAT_BRASS, NULL }, { 'V', IPATCH_SLI_INST_CAT_VOICE, NULL }, { 'O', IPATCH_SLI_INST_CAT_ORGAN, NULL }, { 'W', IPATCH_SLI_INST_CAT_WIND, NULL }, { 'C', IPATCH_SLI_INST_CAT_PERCUSSIVE, NULL }, { 'E', IPATCH_SLI_INST_CAT_ETHNIC, NULL }, { 'T', IPATCH_SLI_INST_CAT_TEXTURE, NULL }, { 'X', IPATCH_SLI_INST_CAT_EFFECTS, NULL }, { 'M', IPATCH_SLI_INST_CAT_MULTI, NULL }, { 'N', IPATCH_SLI_INST_CAT_FB_LOOP, NULL }, { 'I', IPATCH_SLI_INST_CAT_EXT_IN, NULL }, { '@', IPATCH_SLI_INST_CAT_OTHER, NULL } }; static const IpatchSLIInstCatMapEntry ipatch_sli_inst_subcat_map_dsynth[] = { { 'S', IPATCH_SLI_INST_CAT_SYNTH_BASS, NULL }, { 'B', IPATCH_SLI_INST_CAT_BASS, NULL }, { 'Q', IPATCH_SLI_INST_CAT_SEQUENCER, NULL }, { 'U', IPATCH_SLI_INST_CAT_PLUG, NULL }, { 'A', IPATCH_SLI_INST_CAT_ATTACK, NULL }, { 'R', IPATCH_SLI_INST_CAT_RELEASE, NULL }, { 'L', IPATCH_SLI_INST_CAT_LEAD, NULL }, { 'P', IPATCH_SLI_INST_CAT_PAD, NULL }, { 'G', IPATCH_SLI_INST_CAT_STRING, NULL }, { 'F', IPATCH_SLI_INST_CAT_BRASS, NULL }, { 'V', IPATCH_SLI_INST_CAT_VOICE, NULL }, { 'O', IPATCH_SLI_INST_CAT_ORGAN, NULL }, { 'W', IPATCH_SLI_INST_CAT_WIND, NULL }, { 'I', IPATCH_SLI_INST_CAT_PIANO, NULL }, { 'C', IPATCH_SLI_INST_CAT_PERCUSSIVE, NULL }, { 'E', IPATCH_SLI_INST_CAT_ETHNIC, NULL }, { 'T', IPATCH_SLI_INST_CAT_TEXTURE, NULL }, { 'X', IPATCH_SLI_INST_CAT_EFFECTS, NULL }, { '@', IPATCH_SLI_INST_CAT_OTHER, NULL } }; const IpatchSLIInstCatMapEntry ipatch_sli_inst_cat_map[] = { { 'K', IPATCH_SLI_INST_CAT_KICK, ipatch_sli_inst_subcat_map_default }, { 'S', IPATCH_SLI_INST_CAT_SNARE, ipatch_sli_inst_subcat_map_default }, { 'H', IPATCH_SLI_INST_CAT_HIHAT, ipatch_sli_inst_subcat_map_default }, { 'O', IPATCH_SLI_INST_CAT_TOM, ipatch_sli_inst_subcat_map_default }, { 'Y', IPATCH_SLI_INST_CAT_CYMBAL, ipatch_sli_inst_subcat_map_cymbal }, { 'P', IPATCH_SLI_INST_CAT_PERCUSSION, ipatch_sli_inst_subcat_map_percussion }, { 'd', IPATCH_SLI_INST_CAT_DRUM_LOOP, ipatch_sli_inst_subcat_map_default }, { 'p', IPATCH_SLI_INST_CAT_PERC_LOOP, ipatch_sli_inst_subcat_map_default }, { 't', IPATCH_SLI_INST_CAT_TONAL_LOOP, ipatch_sli_inst_subcat_map_default }, { 'x', IPATCH_SLI_INST_CAT_FX_LOOP, NULL }, { 'A', IPATCH_SLI_INST_CAT_A_SYNTH, ipatch_sli_inst_subcat_map_asynth }, { 'D', IPATCH_SLI_INST_CAT_D_SYNTH, ipatch_sli_inst_subcat_map_dsynth }, { '@', IPATCH_SLI_INST_CAT_OTHER, NULL } }; /** * Getter fonction to access ipatch_sli_inst_cat_map[] table * @return pointer on ipatch_sli_inst_cat_map[] table */ const IpatchSLIInstCatMapEntry * ipatch_sli_inst_get_cat_map(void) { return ipatch_sli_inst_cat_map; } /** * Getter fonction to access ipatch_sli_inst_cat_map[] table * @return pointer on ipatch_sli_inst_cat_strings[] table */ const char **ipatch_sli_inst_get_cat_strings(void) { return ipatch_sli_inst_cat_strings; } libinstpatch-1.1.6/libinstpatch/IpatchSLIReader.c000066400000000000000000000620611400263525300217430ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSLIReader * @short_description: Spectralis file reader * @see_also: * * Reads a Spectralis SLI or SLC file and loads it into a object tree * (#IpatchSLI). */ #include #include #include #include #include #include "IpatchRiff.h" #include "IpatchSLIReader.h" #include "IpatchSampleStore.h" #include "IpatchSampleStoreRam.h" #include "IpatchSampleStoreFile.h" #include "IpatchSLIFile_priv.h" #include "IpatchUnit.h" #include "ipatch_priv.h" #include "i18n.h" /* ----- ** WARNING ** ----- We don't care about locking in here, because we are the exclusive owner of the loading SoundFont. Many fields are accessed directly, etc. This is not an example of proper use of libInstPatch.. Don't try this at home.. Blah blah. */ static void ipatch_sli_reader_class_init(IpatchSLIReaderClass *klass); static void ipatch_sli_reader_init(IpatchSLIReader *reader); static void ipatch_sli_reader_finalize(GObject *object); static GQuark ipatch_sli_reader_error_quark(void); static gboolean ipatch_sli_load_level_0(IpatchSLIReader *reader, GError **err); static void ipatch_sli_load_siig(IpatchFileHandle *handle, IpatchSLISiIg *siig); static void ipatch_sli_load_ihdr(IpatchFileHandle *handle, IpatchSLIInstHeader *ihdr); static void ipatch_sli_load_shdr(IpatchFileHandle *handle, IpatchSLISampleHeader *shdr); static void ipatch_sli_set_gen(IpatchSLIZone *zone, const int genid, IpatchSF2GenAmount *amount); static guint32 ipatch_sli_load_zone(IpatchFileHandle *handle, IpatchSLIZone *zone); static IpatchSLISample *ipatch_sli_load_sample(IpatchFileHandle *handle, guint32 smpdata_offs); #define SLI_ERROR_MSG "Spectralis reader error: %s" G_DEFINE_TYPE(IpatchSLIReader, ipatch_sli_reader, G_TYPE_OBJECT) static void ipatch_sli_reader_class_init(IpatchSLIReaderClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->finalize = ipatch_sli_reader_finalize; } static void ipatch_sli_reader_init(IpatchSLIReader *reader) { reader->handle = NULL; reader->sli = NULL; } static void ipatch_sli_reader_finalize(GObject *object) { IpatchSLIReader *reader = IPATCH_SLI_READER(object); if(reader->handle) { ipatch_file_close(reader->handle); /* -- unref file object */ } if(reader->sli) { g_object_unref(reader->sli); /* -- unref SLI */ reader->sli = NULL; } if(G_OBJECT_CLASS(ipatch_sli_reader_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_sli_reader_parent_class)->finalize(object); } } static GQuark ipatch_sli_reader_error_quark(void) { static GQuark q = 0; if(q == 0) { q = g_quark_from_static_string("sli-reader-error-quark"); } return (q); } /** * ipatch_sli_reader_new: * @handle: Spectralis file handle to parse or %NULL to set later * * Create a new Spectralis file reader * * Returns: The new Spectralis file reader */ IpatchSLIReader * ipatch_sli_reader_new(IpatchFileHandle *handle) { IpatchSLIReader *reader; g_return_val_if_fail(!handle || IPATCH_IS_SLI_FILE(handle->file), NULL); reader = g_object_new(IPATCH_TYPE_SLI_READER, NULL); ipatch_sli_reader_set_file_handle(reader, handle); return (reader); } /** * ipatch_sli_reader_set_file_handle: * @reader: Spectralis reader object * @handle: Spectralis file handle * * Set the Spectralis file handle of a Spectralis reader. */ void ipatch_sli_reader_set_file_handle(IpatchSLIReader *reader, IpatchFileHandle *handle) { g_return_if_fail(IPATCH_IS_SLI_READER(reader)); g_return_if_fail(handle && IPATCH_IS_SLI_FILE(handle->file)); /* Close old handle, if any */ if(reader->handle) { ipatch_file_close(reader->handle); } reader->handle = handle; } /** * ipatch_sli_reader_load: * @reader: Spectralis reader object * @err: Location to store error info or %NULL * * Load a Spectralis file. * * Returns: (transfer full): New #IpatchSLI object with refcount of 1. */ IpatchSLI * ipatch_sli_reader_load(IpatchSLIReader *reader, GError **err) { GError *local_err = NULL; guint32 buf[IPATCH_RIFF_HEADER_SIZE / sizeof(guint32)]; int size; g_return_val_if_fail(IPATCH_IS_SLI_READER(reader), NULL); g_return_val_if_fail(reader->handle && IPATCH_IS_SLI_FILE(reader->handle->file), NULL); g_return_val_if_fail(!err || !*err, NULL); /* read ID and chunk size */ if(!ipatch_file_read(reader->handle, buf, IPATCH_RIFF_HEADER_SIZE, err)) { return (NULL); } if(buf[0] != IPATCH_SLI_FOURCC_SIFI) { char idstr[4]; memcpy(idstr, buf, 4); g_set_error(err, ipatch_sli_reader_error_quark(), IPATCH_RIFF_ERROR_UNEXPECTED_ID, _("Not a Spectralis file (RIFF id = '%4s')"), idstr); return (NULL); } /* verify total size of file with RIFF chunk size */ size = ipatch_file_get_size(reader->handle->file, &local_err); if(size == -1) { g_warning("Spectralis file size check failed: %s", ipatch_gerror_message(local_err)); g_clear_error(&local_err); } else if(size != buf[1]) { g_set_error(err, ipatch_sli_reader_error_quark(), IPATCH_RIFF_ERROR_SIZE_MISMATCH, _("File size mismatch (chunk size = %d, actual = %d)"), buf[1], size); return (NULL); } reader->sli = ipatch_sli_new(); /* ++ ref new object */ ipatch_sli_set_file(reader->sli, IPATCH_SLI_FILE(reader->handle->file)); if(!ipatch_file_skip(reader->handle, IPATCH_SLI_SIFI_SIZE, err) || !ipatch_sli_load_level_0(reader, err)) { g_object_unref(reader->sli); reader->sli = NULL; return (NULL); } ipatch_item_clear_flags(IPATCH_ITEM(reader->sli), IPATCH_BASE_SAVED | IPATCH_BASE_CHANGED); /* ++ ref for caller, finalize() will remove reader's reference */ return (g_object_ref(reader->sli)); } static gboolean ipatch_sli_load_level_0(IpatchSLIReader *reader, GError **err) { GError *local_err = NULL; guint32 pos, size; IpatchSLISiIg siig; IpatchSLIInstHeader ihdr; IpatchSLIInst *inst; IpatchSLIZone *zone; IpatchIter inst_iter, zone_iter, smpl_iter; unsigned int i, z, sample_idx; IpatchSLISample **sample_map; /* initialize iterator to instrument list */ if(!ipatch_container_init_iter(IPATCH_CONTAINER(reader->sli), &inst_iter, IPATCH_TYPE_SLI_INST)) { return (FALSE); } /* initialize iterator to sample list */ if(!ipatch_container_init_iter(IPATCH_CONTAINER(reader->sli), &smpl_iter, IPATCH_TYPE_SLI_SAMPLE)) { return (FALSE); } /* clear siig just to silence the compiler */ memset(&siig, 0, sizeof(siig)); size = ipatch_file_get_size(reader->handle->file, &local_err); pos = ipatch_file_get_position(reader->handle); while(size > pos) { /* get headers */ if(!ipatch_file_buf_load(reader->handle, IPATCH_SLI_HEAD_SIZE, err)) { return (FALSE); } /* load next SiIg header */ ipatch_sli_load_siig(reader->handle, &siig); if(siig.ckid != IPATCH_SLI_FOURCC_SIIG) { char idstr[4]; memcpy(idstr, &siig.ckid, 4); g_set_error(err, ipatch_sli_reader_error_quark(), IPATCH_RIFF_ERROR_UNEXPECTED_ID, _("Not an instument group header (RIFF id = '%4s', position = %d)"), idstr, pos); return (FALSE); } if(siig.cklen > size - pos) /* verify chunk size */ { g_set_error(err, ipatch_sli_reader_error_quark(), IPATCH_RIFF_ERROR_SIZE_MISMATCH, _(SLI_ERROR_MSG), "Unexpected chunk size in instument group header"); return (FALSE); } if(!siig.instnum) /* empty inst group? */ { goto skip; } sample_map = g_malloc0(siig.maxzones_num * sizeof(gpointer)); /* loop over all instrument headers */ for(i = 0; i < siig.instnum; i++) { ipatch_file_buf_seek(reader->handle, siig.inst_offs + i * IPATCH_SLI_INST_SIZE, G_SEEK_SET); ipatch_sli_load_ihdr(reader->handle, &ihdr); inst = ipatch_sli_inst_new(); /* ++ ref new instrument */ inst->name = g_strndup(ihdr.name, IPATCH_SLI_NAME_SIZE); inst->sound_id = ihdr.sound_id; inst->category = ihdr.category; /* insert the instrument (default is to keep appending) */ ipatch_container_insert_iter((IpatchContainer *)(reader->sli), (IpatchItem *)inst, &inst_iter); g_object_unref(inst); /* -- unref new instrument */ /* initialize iterator to instrument zone list */ if(!ipatch_container_init_iter((IpatchContainer *)inst, &zone_iter, IPATCH_TYPE_SLI_ZONE)) { return (FALSE); } for(z = 0; z < ihdr.zones_num; z++) { /* read zone headers */ ipatch_file_buf_seek(reader->handle, siig.zones_offs + (ihdr.zone_idx + z) * IPATCH_SLI_ZONE_SIZE, G_SEEK_SET); zone = g_object_new(IPATCH_TYPE_SLI_ZONE, NULL); /* ++ ref new zone */ sample_idx = ipatch_sli_load_zone(reader->handle, zone); ipatch_container_insert_iter((IpatchContainer *)inst, (IpatchItem *)zone, &zone_iter); if(sample_idx >= siig.maxzones_num) { g_set_error(err, ipatch_sli_reader_error_quark(), IPATCH_RIFF_ERROR_INVALID_DATA, "Sample index is too large in zone %d of inst '%s'", z, inst->name); return (FALSE); } /* use existing or create new sample from corresponding sample header */ if(!sample_map[sample_idx]) { ipatch_file_buf_seek(reader->handle, siig.smphdr_offs + sample_idx * IPATCH_SLI_SMPL_SIZE, G_SEEK_SET); /* ++ ref new sample */ sample_map[sample_idx] = ipatch_sli_load_sample(reader->handle, pos + siig.smpdata_offs); /* insert sample */ ipatch_container_insert_iter((IpatchContainer *)(reader->sli), (IpatchItem *)sample_map[sample_idx], &smpl_iter); } ipatch_sli_zone_set_sample(zone, sample_map[sample_idx]); g_object_unref(zone); /* -- unref new zone */ } /* for (zone) */ } /* for (inst) */ for(sample_idx = 0; sample_idx < siig.maxzones_num; sample_idx++) if(!sample_map[sample_idx]) { g_object_unref(sample_map[sample_idx]); /* -- unref new sample */ } g_free(sample_map); skip: /* seek to the end of the current chunk */ if(!ipatch_file_seek(reader->handle, pos + siig.cklen, G_SEEK_SET, err)) { return (FALSE); } /* skip SiDp chunks (one for each instrument) */ if(!ipatch_file_skip(reader->handle, siig.instnum * IPATCH_SLI_SIDP_SIZE, err)) { return (FALSE); } pos = ipatch_file_get_position(reader->handle); } /* while (chunks) */ return (TRUE); } static void ipatch_sli_load_siig(IpatchFileHandle *handle, IpatchSLISiIg *siig) { g_return_if_fail(handle != NULL); g_return_if_fail(siig != NULL); siig->ckid = ipatch_file_buf_read_u32(handle); siig->cklen = ipatch_file_buf_read_u32(handle); siig->spechdr = ipatch_file_buf_read_u16(handle); siig->unused1 = ipatch_file_buf_read_u16(handle); siig->inst_offs = ipatch_file_buf_read_u16(handle); siig->instnum = ipatch_file_buf_read_u16(handle); siig->zones_offs = ipatch_file_buf_read_u16(handle); siig->allzones_num = ipatch_file_buf_read_u16(handle); siig->smphdr_offs = ipatch_file_buf_read_u16(handle); siig->maxzones_num = ipatch_file_buf_read_u16(handle); siig->smpdata_offs = ipatch_file_buf_read_u16(handle); siig->unused2 = ipatch_file_buf_read_u16(handle); } static void ipatch_sli_load_ihdr(IpatchFileHandle *handle, IpatchSLIInstHeader *ihdr) { g_return_if_fail(handle != NULL); g_return_if_fail(ihdr != NULL); ipatch_file_buf_read(handle, &ihdr->name, IPATCH_SLI_NAME_SIZE); ihdr->sound_id = ipatch_file_buf_read_u32(handle); ihdr->unused1 = ipatch_file_buf_read_u32(handle); ihdr->category = ipatch_file_buf_read_u16(handle); ihdr->unused2 = ipatch_file_buf_read_u16(handle); ihdr->zone_idx = ipatch_file_buf_read_u16(handle); ihdr->zones_num = ipatch_file_buf_read_u16(handle); } static void ipatch_sli_load_shdr(IpatchFileHandle *handle, IpatchSLISampleHeader *shdr) { g_return_if_fail(handle != NULL); g_return_if_fail(shdr != NULL); ipatch_file_buf_read(handle, &shdr->name, IPATCH_SLI_NAME_SIZE); shdr->start = ipatch_file_buf_read_u32(handle); shdr->end = ipatch_file_buf_read_u32(handle); shdr->loop_start = ipatch_file_buf_read_u32(handle); shdr->loop_end = ipatch_file_buf_read_u32(handle); shdr->fine_tune = ipatch_file_buf_read_s8(handle); shdr->root_note = ipatch_file_buf_read_u8(handle); shdr->channels = ipatch_file_buf_read_u8(handle); shdr->bits_per_sample = ipatch_file_buf_read_u8(handle); shdr->sample_rate = ipatch_file_buf_read_u32(handle); } static void ipatch_sli_set_gen(IpatchSLIZone *zone, const int genid, IpatchSF2GenAmount *amount) { IpatchSF2GenAmount def_amount; g_return_if_fail(zone != NULL); g_return_if_fail(genid >= 0 && genid < IPATCH_SF2_GEN_COUNT); g_return_if_fail(amount != NULL); /* check if amount is different from default... */ ipatch_sf2_gen_default_value(genid, FALSE, &def_amount); if(amount->sword != def_amount.sword) { /* ...and set it on zone if so */ zone->genarray.values[genid] = *amount; IPATCH_SF2_GEN_ARRAY_SET_FLAG(&zone->genarray, genid); } } static guint32 ipatch_sli_load_zone(IpatchFileHandle *handle, IpatchSLIZone *zone) { IpatchSF2GenAmount amount; guint32 offs; amount.range.low = ipatch_file_buf_read_u8(handle); /* keyrange_low */ amount.range.high = ipatch_file_buf_read_u8(handle); /* keyrange_high */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_NOTE_RANGE, &amount); amount.range.low = ipatch_file_buf_read_u8(handle); /* velrange_low */ amount.range.high = ipatch_file_buf_read_u8(handle); /* velrange_high */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_VELOCITY_RANGE, &amount); offs = ipatch_file_buf_read_u32(handle); /* start_offs1 */ if(offs != ipatch_file_buf_read_u32(handle)) /* start_offs2 */ { g_warning(_("Ignoring different 2nd start offset for zone")); } amount.uword = offs >> 16; ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_SAMPLE_COARSE_START, &amount); amount.uword = (guint16)offs / 2; ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_SAMPLE_START, &amount); offs = ipatch_file_buf_read_u32(handle); /* unknown1 */ if(offs) { g_warning(_("Ignoring 1st unknown value for zone")); } offs = ipatch_file_buf_read_u32(handle); /* unknown2 */ if(offs) { g_warning(_("Ignoring 2nd unknown value for zone")); } amount.sword = ipatch_file_buf_read_s8(handle); /* coarse_tune1 */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_COARSE_TUNE, &amount); amount.sword = ipatch_file_buf_read_s8(handle); /* fine_tune1 */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE, &amount); zone->flags = ipatch_file_buf_read_u8(handle); /* sample_modes */ if(zone->flags & IPATCH_SF2_GEN_SAMPLE_MODE_LOOP) { amount.uword = IPATCH_SF2_GEN_SAMPLE_MODE_LOOP; ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_SAMPLE_MODES, &amount); } amount.sword = ipatch_file_buf_read_s8(handle); /* root_note */ if(amount.sword) { ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE, &amount); } amount.uword = ipatch_file_buf_read_u16(handle); /* scale_tuning */ if(amount.uword) { ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_SCALE_TUNE, &amount); } amount = IPATCH_SLI_ZONE_GEN_AMT(zone, IPATCH_SF2_GEN_COARSE_TUNE); if(amount.sword != ipatch_file_buf_read_s8(handle)) /* coarse_tune2 */ { g_warning(_("Ignoring different 2nd coarse tune value for zone")); } amount = IPATCH_SLI_ZONE_GEN_AMT(zone, IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE); if(amount.sword != ipatch_file_buf_read_s8(handle)) /* fine_tune2 */ { g_warning(_("Ignoring different 2nd fine tune value for zone")); } amount.sword = ipatch_file_buf_read_s16(handle); /* modLfoToPitch */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_MOD_LFO_TO_PITCH, &amount); amount.sword = ipatch_file_buf_read_s16(handle); /* vibLfoToPitch */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_VIB_LFO_TO_PITCH, &amount); amount.sword = ipatch_file_buf_read_s16(handle); /* modEnvToPitch */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_MOD_ENV_TO_PITCH, &amount); amount.uword = ipatch_file_buf_read_u16(handle); /* initialFilterFc */ if(amount.uword) { ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_FILTER_CUTOFF, &amount); } amount.uword = ipatch_file_buf_read_u16(handle); /* initialFilterQ */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_FILTER_Q, &amount); amount.sword = ipatch_file_buf_read_s16(handle); /* modLfoToFilterFc */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_MOD_LFO_TO_FILTER_CUTOFF, &amount); amount.sword = ipatch_file_buf_read_s16(handle); /* modEnvToFilterFc */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_MOD_ENV_TO_FILTER_CUTOFF, &amount); amount.sword = ipatch_file_buf_read_s16(handle); /* modLfoToVolume */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_MOD_LFO_TO_VOLUME, &amount); amount.sword = ipatch_file_buf_read_s16(handle); /* freqModLfo */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_MOD_LFO_FREQ, &amount); amount.sword = ipatch_file_buf_read_s16(handle); /* freqVibLfo */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_VIB_LFO_FREQ, &amount); amount.uword = ipatch_file_buf_read_u16(handle); /* sustainModEnv */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_MOD_ENV_SUSTAIN, &amount); amount.sword = ipatch_file_buf_read_s16(handle); /* keynumToModEnvHold */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_HOLD, &amount); amount.sword = ipatch_file_buf_read_s16(handle); /* keynumToModEnvDecay */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_DECAY, &amount); amount.uword = ipatch_file_buf_read_u16(handle); /* sustainVolEnv */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_VOL_ENV_SUSTAIN, &amount); amount.sword = ipatch_file_buf_read_s16(handle); /* keynumToVolEnvHold */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_HOLD, &amount); amount.sword = ipatch_file_buf_read_s16(handle); /* keynumToVolEnvDecay */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_DECAY, &amount); amount.sword = ipatch_file_buf_read_s8(handle) * 5; /* pan */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_PAN, &amount); amount.sword = ipatch_file_buf_read_s8(handle) * 100; /* delayModLfo */ if(amount.sword) { ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_MOD_LFO_DELAY, &amount); } amount.sword = ipatch_file_buf_read_s8(handle) * 100; /* delayVibLfo */ if(amount.sword) { ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_VIB_LFO_DELAY, &amount); } amount.sword = ipatch_file_buf_read_s8(handle) * 100; /* attackModEnv */ if(amount.sword) { ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_MOD_ENV_ATTACK, &amount); } amount.sword = ipatch_file_buf_read_s8(handle) * 100; /* holdModEnv */ if(amount.sword) { ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_MOD_ENV_HOLD, &amount); } amount.sword = ipatch_file_buf_read_s8(handle) * 100; /* decayModEnv */ if(amount.sword) { ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_MOD_ENV_DECAY, &amount); } amount.sword = ipatch_file_buf_read_s8(handle) * 100; /* releaseModEnv */ if(amount.sword) { ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_MOD_ENV_RELEASE, &amount); } amount.sword = ipatch_file_buf_read_s8(handle) * 100; /* attackVolEnv */ if(amount.sword) { ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_VOL_ENV_ATTACK, &amount); } amount.sword = ipatch_file_buf_read_s8(handle) * 100; /* holdVolEnv */ if(amount.sword) { ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_VOL_ENV_HOLD, &amount); } amount.sword = ipatch_file_buf_read_s8(handle) * 100; /* decayVolEnv */ if(amount.sword) { ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_VOL_ENV_DECAY, &amount); } amount.sword = ipatch_file_buf_read_s8(handle) * 100; /* releaseVolEnv */ if(amount.sword) { ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_VOL_ENV_RELEASE, &amount); } amount.uword = ipatch_file_buf_read_u8(handle) * 10; /* initialAttenuation */ ipatch_sli_set_gen(zone, IPATCH_SF2_GEN_ATTENUATION, &amount); return ipatch_file_buf_read_u16(handle); } static IpatchSLISample * ipatch_sli_load_sample(IpatchFileHandle *handle, guint32 smpdata_offs) { IpatchSLISampleHeader shdr; IpatchSLISample *sample; IpatchSample *store; IpatchSampleData *sampledata; guint bytes_per_sample, length; int format; ipatch_sli_load_shdr(handle, &shdr); sample = g_object_new(IPATCH_TYPE_SLI_SAMPLE, NULL); /* ++ ref new sample */ sample->name = g_strndup(shdr.name, IPATCH_SLI_NAME_SIZE); /* some basic sanity check of sample header data */ if(shdr.start > shdr.end || shdr.end - shdr.start < 48) { g_warning(_("Invalid sample '%s'"), sample->name); goto bad; } bytes_per_sample = shdr.bits_per_sample / 8; length = shdr.end - shdr.start; if(shdr.loop_start <= shdr.loop_end && shdr.loop_start <= length && shdr.loop_end <= length) { /* loop is okay? */ sample->loop_start = shdr.loop_start / bytes_per_sample; sample->loop_end = shdr.loop_end / bytes_per_sample; } else /* loop is bad */ { g_warning(_("Invalid loop for sample '%s'"), sample->name); } sample->rate = shdr.sample_rate; sample->root_note = shdr.root_note; sample->fine_tune = shdr.fine_tune; format = (shdr.bits_per_sample == 8 ? IPATCH_SAMPLE_8BIT : IPATCH_SAMPLE_16BIT) | (shdr.channels == 2 ? IPATCH_SAMPLE_STEREO : IPATCH_SAMPLE_MONO) | IPATCH_SAMPLE_SIGNED | IPATCH_SAMPLE_LENDIAN; store = ipatch_sample_store_file_new(handle->file, /* ++ ref sample store */ smpdata_offs + shdr.start); g_object_set(store, "sample-size", length / bytes_per_sample / shdr.channels, "sample-format", format, "sample-rate", sample->rate, NULL); sampledata = ipatch_sample_data_new(); /* ++ ref sampledata */ ipatch_sample_data_add(sampledata, IPATCH_SAMPLE_STORE(store)); ipatch_sli_sample_set_data(sample, sampledata); g_object_unref(store); /* -- unref sample store */ g_object_unref(sampledata); /* -- unref sampledata */ return sample; /* !! pass on ref to caller */ bad: ipatch_sli_sample_set_blank(sample); return sample; /* !! pass on ref to caller */ } libinstpatch-1.1.6/libinstpatch/IpatchSLIReader.h000066400000000000000000000047351400263525300217540ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SLI_READER_H__ #define __IPATCH_SLI_READER_H__ #include #include #include typedef struct _IpatchSLIReader IpatchSLIReader; /* private structure */ typedef struct _IpatchSLIReaderClass IpatchSLIReaderClass; #define IPATCH_TYPE_SLI_READER (ipatch_sli_reader_get_type ()) #define IPATCH_SLI_READER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SLI_READER, \ IpatchSLIReader)) #define IPATCH_SLI_READER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SLI_READER, \ IpatchSLIReaderClass)) #define IPATCH_IS_SLI_READER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SLI_READER)) #define IPATCH_IS_SLI_READER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SLI_READER)) /* Spectralis SLI/SLC file parser object * Spectralis files do not sufficiently follow RIFF format * so this object is not derived from IpatchRiff class */ struct _IpatchSLIReader { GObject parent_instance; /* derived from GObject */ IpatchFileHandle *handle; /* file object being parsed */ IpatchSLI *sli; /* Spectralis object to load file into */ }; /* Spectralis SLI/SLC file parser class */ struct _IpatchSLIReaderClass { GObjectClass parent_class; }; GType ipatch_sli_reader_get_type(void); IpatchSLIReader *ipatch_sli_reader_new(IpatchFileHandle *handle); void ipatch_sli_reader_set_file_handle(IpatchSLIReader *reader, IpatchFileHandle *handle); IpatchSLI *ipatch_sli_reader_load(IpatchSLIReader *reader, GError **err); #endif libinstpatch-1.1.6/libinstpatch/IpatchSLISample.c000066400000000000000000000514261400263525300217650ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSLISample * @short_description: Spectralis audio sample * @see_also: #IpatchSLI, #IpatchSLIZone * * Spectralis samples are children of #IpatchSLI objects and are referenced * by #IpatchSLIZone objects. They define the audio which is synthesized. */ #include #include #include #include "IpatchSLISample.h" #include "IpatchSample.h" #include "IpatchSampleData.h" #include "IpatchSLI.h" #include "IpatchSLIFile.h" #include "IpatchParamProp.h" #include "IpatchTypeProp.h" #include "IpatchSF2VoiceCache.h" #include "ipatch_priv.h" #include "builtin_enums.h" /* properties */ enum { PROP_0, PROP_NAME, PROP_SAMPLE_SIZE, PROP_SAMPLE_FORMAT, PROP_SAMPLE_RATE, PROP_LOOP_TYPE, PROP_LOOP_START, PROP_LOOP_END, PROP_ROOT_NOTE, PROP_FINE_TUNE, PROP_SAMPLE_DATA, }; static void ipatch_sli_sample_iface_init(IpatchSampleIface *sample_iface); static gboolean ipatch_sli_sample_iface_open(IpatchSampleHandle *handle, GError **err); static void ipatch_sli_sample_finalize(GObject *gobject); static void ipatch_sli_sample_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sli_sample_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sli_sample_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static void ipatch_sli_sample_item_remove_full(IpatchItem *item, gboolean full); static int ipatch_sli_sample_voice_cache_update_handler(IpatchSF2VoiceCache *cache, int *select_values, GObject *cache_item, GObject *item, GParamSpec *pspec, const GValue *value, IpatchSF2VoiceUpdate *updates, guint max_updates); static void ipatch_sli_sample_real_set_name(IpatchSLISample *sample, const char *name, gboolean name_notify); static void ipatch_sli_sample_real_set_data(IpatchSLISample *sample, IpatchSampleData *sampledata, gboolean data_notify); /* cached parameter spec values for speed */ static GParamSpec *name_pspec; static GParamSpec *sample_data_pspec; G_DEFINE_TYPE_WITH_CODE(IpatchSLISample, ipatch_sli_sample, IPATCH_TYPE_ITEM, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_sli_sample_iface_init)) /* sample interface initialization */ static void ipatch_sli_sample_iface_init(IpatchSampleIface *sample_iface) { sample_iface->open = ipatch_sli_sample_iface_open; } static gboolean ipatch_sli_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchSLISample *sample = IPATCH_SLI_SAMPLE(handle->sample); g_return_val_if_fail(sample->sample_data != NULL, FALSE); return (ipatch_sample_handle_cascade_open (handle, (IpatchSample *)(sample->sample_data), err)); } static void ipatch_sli_sample_class_init(IpatchSLISampleClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); GParamSpec *pspec; obj_class->finalize = ipatch_sli_sample_finalize; obj_class->get_property = ipatch_sli_sample_get_property; /* we use the IpatchItem item_set_property method */ item_class->item_set_property = ipatch_sli_sample_set_property; item_class->copy = ipatch_sli_sample_item_copy; item_class->remove_full = ipatch_sli_sample_item_remove_full; /* "name" property is used as the title */ g_object_class_override_property(obj_class, PROP_NAME, "title"); name_pspec = g_param_spec_string("name", _("Name"), _("Name"), NULL, G_PARAM_READWRITE | IPATCH_PARAM_UNIQUE); ipatch_param_set(name_pspec, "string-max-length", IPATCH_SLI_NAME_SIZE, /* max length */ NULL); g_object_class_install_property(obj_class, PROP_NAME, name_pspec); /* properties defined by IpatchSample interface */ ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_SIZE, "sample-size"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_FORMAT, "sample-format"); pspec = ipatch_sample_install_property(obj_class, PROP_SAMPLE_RATE, "sample-rate"); pspec->flags |= IPATCH_PARAM_SYNTH; /* IpatchSLISample objects don't have a loop type field really */ ipatch_sample_install_property_readonly(obj_class, PROP_LOOP_TYPE, "loop-type"); pspec = ipatch_sample_install_property(obj_class, PROP_LOOP_START, "loop-start"); pspec->flags |= IPATCH_PARAM_SYNTH | IPATCH_PARAM_SYNTH_REALTIME; pspec = ipatch_sample_install_property(obj_class, PROP_LOOP_END, "loop-end"); pspec->flags |= IPATCH_PARAM_SYNTH | IPATCH_PARAM_SYNTH_REALTIME; pspec = ipatch_sample_install_property(obj_class, PROP_ROOT_NOTE, "root-note"); pspec->flags |= IPATCH_PARAM_SYNTH; pspec = ipatch_sample_install_property(obj_class, PROP_FINE_TUNE, "fine-tune"); pspec->flags |= IPATCH_PARAM_SYNTH | IPATCH_PARAM_SYNTH_REALTIME; sample_data_pspec = ipatch_sample_install_property(obj_class, PROP_SAMPLE_DATA, "sample-data"); /* install IpatchSF2VoiceCache update handler for real time effects */ ipatch_type_set(IPATCH_TYPE_SLI_SAMPLE, "sf2voice-update-func", ipatch_sli_sample_voice_cache_update_handler, NULL); } static void ipatch_sli_sample_init(IpatchSLISample *sample) { ipatch_sli_sample_set_blank(sample); sample->rate = IPATCH_SAMPLE_RATE_DEFAULT; } static void ipatch_sli_sample_finalize(GObject *gobject) { IpatchSLISample *sample = IPATCH_SLI_SAMPLE(gobject); /* nothing should reference the sample after this, but we set pointers to NULL to help catch invalid references. Locking of sample is required since in reality all its children do still hold references */ ipatch_sli_sample_real_set_data(sample, NULL, FALSE); IPATCH_ITEM_WLOCK(sample); g_free(sample->name); sample->name = NULL; IPATCH_ITEM_WUNLOCK(sample); if(G_OBJECT_CLASS(ipatch_sli_sample_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_sli_sample_parent_class)->finalize(gobject); } } static void ipatch_sli_sample_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSLISample *sample; g_return_if_fail(IPATCH_IS_SLI_SAMPLE(object)); sample = IPATCH_SLI_SAMPLE(object); switch(property_id) { case PROP_NAME: ipatch_sli_sample_real_set_name(sample, g_value_get_string(value), FALSE); /* don't do name notify */ break; case PROP_SAMPLE_RATE: IPATCH_ITEM_WLOCK(sample); sample->rate = g_value_get_int(value); IPATCH_ITEM_WUNLOCK(sample); break; case PROP_LOOP_START: IPATCH_ITEM_WLOCK(sample); sample->loop_start = g_value_get_uint(value); IPATCH_ITEM_WUNLOCK(sample); break; case PROP_LOOP_END: IPATCH_ITEM_WLOCK(sample); sample->loop_end = g_value_get_uint(value); IPATCH_ITEM_WUNLOCK(sample); break; case PROP_ROOT_NOTE: IPATCH_ITEM_WLOCK(sample); sample->root_note = g_value_get_int(value); IPATCH_ITEM_WUNLOCK(sample); break; case PROP_FINE_TUNE: IPATCH_ITEM_WLOCK(sample); sample->fine_tune = g_value_get_int(value); IPATCH_ITEM_WUNLOCK(sample); break; case PROP_SAMPLE_DATA: ipatch_sli_sample_real_set_data(sample, (IpatchSampleData *) g_value_get_object(value), FALSE); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } static void ipatch_sli_sample_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSLISample *sample; g_return_if_fail(IPATCH_IS_SLI_SAMPLE(object)); sample = IPATCH_SLI_SAMPLE(object); switch(property_id) { case PROP_NAME: g_value_take_string(value, ipatch_sli_sample_get_name(sample)); break; case PROP_SAMPLE_SIZE: g_return_if_fail(sample->sample_data != NULL); g_value_set_uint(value, ipatch_sample_get_size((IpatchSample *)(sample->sample_data), NULL)); break; case PROP_SAMPLE_FORMAT: g_return_if_fail(sample->sample_data != NULL); g_value_set_int(value, ipatch_sample_get_format((IpatchSample *)(sample->sample_data))); break; case PROP_SAMPLE_RATE: IPATCH_ITEM_RLOCK(sample); g_value_set_int(value, sample->rate); IPATCH_ITEM_RUNLOCK(sample); break; case PROP_LOOP_TYPE: /* IpatchSLISample objects don't have loop type, just use normal loop */ g_value_set_enum(value, IPATCH_SAMPLE_LOOP_STANDARD); break; case PROP_LOOP_START: IPATCH_ITEM_RLOCK(sample); g_value_set_uint(value, sample->loop_start); IPATCH_ITEM_RUNLOCK(sample); break; case PROP_LOOP_END: IPATCH_ITEM_RLOCK(sample); g_value_set_uint(value, sample->loop_end); IPATCH_ITEM_RUNLOCK(sample); break; case PROP_ROOT_NOTE: g_value_set_int(value, sample->root_note); break; case PROP_FINE_TUNE: g_value_set_int(value, sample->fine_tune); break; case PROP_SAMPLE_DATA: g_value_take_object(value, ipatch_sli_sample_get_data(sample)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_sli_sample_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchSLISample *src_sam, *dest_sam; src_sam = IPATCH_SLI_SAMPLE(src); dest_sam = IPATCH_SLI_SAMPLE(dest); IPATCH_ITEM_RLOCK(src_sam); ipatch_sli_sample_set_data(dest_sam, src_sam->sample_data); dest_sam->name = g_strdup(src_sam->name); dest_sam->rate = src_sam->rate; dest_sam->loop_start = src_sam->loop_start; dest_sam->loop_end = src_sam->loop_end; dest_sam->root_note = src_sam->root_note; dest_sam->fine_tune = src_sam->fine_tune; IPATCH_ITEM_RUNLOCK(src_sam); } static void ipatch_sli_sample_item_remove_full(IpatchItem *item, gboolean full) { IpatchList *list; IpatchItem *zone, *temp; IpatchIter iter; /* ++ ref zone list */ list = ipatch_sli_get_zone_references(IPATCH_SLI_SAMPLE(item)); ipatch_list_init_iter(list, &iter); zone = ipatch_item_first(&iter); while(zone) { temp = zone; zone = ipatch_item_next(&iter); ipatch_item_remove(temp); } g_object_unref(list); /* -- unref list */ if(full) { ipatch_sli_sample_set_data(IPATCH_SLI_SAMPLE(item), NULL); } if(IPATCH_ITEM_CLASS(ipatch_sli_sample_parent_class)->remove_full) { IPATCH_ITEM_CLASS(ipatch_sli_sample_parent_class)->remove_full(item, full); } } /* IpatchSF2VoiceCache update function for realtime effects */ static int ipatch_sli_sample_voice_cache_update_handler(IpatchSF2VoiceCache *cache, int *select_values, GObject *cache_item, GObject *item, GParamSpec *pspec, const GValue *value, IpatchSF2VoiceUpdate *updates, guint max_updates) { IpatchSF2Voice *voice; guint8 genid, genid2 = 255; gint16 val = 0, val2 = 0; int ival; g_return_val_if_fail(cache->voices->len > 0, 0); voice = IPATCH_SF2_VOICE_CACHE_GET_VOICE(cache, 0); switch(IPATCH_PARAM_SPEC_ID(pspec)) { case PROP_LOOP_START: genid = IPATCH_SF2_GEN_SAMPLE_LOOP_START; genid2 = IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START; ival = (int)g_value_get_uint(value) - voice->loop_start; val = ival % 32768; val2 = ival / 32768; break; case PROP_LOOP_END: genid = IPATCH_SF2_GEN_SAMPLE_LOOP_END; genid2 = IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END; ival = (int)g_value_get_uint(value) - voice->loop_end; val = ival % 32768; val2 = ival / 32768; break; case PROP_FINE_TUNE: genid = IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE; ival = g_value_get_int(value); break; default: return (0); } updates->voice = 0; updates->genid = genid; updates->ival = val; if(genid2 != 255 && max_updates >= 2) { updates[1].voice = 0; updates[1].genid = genid2; updates[1].ival = val2; return (2); } else { return (1); } } /** * ipatch_sli_sample_new: * * Create a new Spectralis sample object. * * Returns: New Spectralis sample with a reference count of 1. Caller * owns the reference and removing it will destroy the item, unless another * reference is added (if its parented for example). */ IpatchSLISample * ipatch_sli_sample_new(void) { return (IPATCH_SLI_SAMPLE(g_object_new(IPATCH_TYPE_SLI_SAMPLE, NULL))); } /** * ipatch_sli_sample_first: (skip) * @iter: Patch item iterator containing #IpatchSLISample items * * Gets the first item in a sample iterator. A convenience wrapper for * ipatch_iter_first(). * * Returns: The first sample in @iter or %NULL if empty. */ IpatchSLISample * ipatch_sli_sample_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_SLI_SAMPLE(obj)); } else { return (NULL); } } /** * ipatch_sli_sample_next: (skip) * @iter: Patch item iterator containing #IpatchSLISample items * * Gets the next item in a sample iterator. A convenience wrapper for * ipatch_iter_next(). * * Returns: The next sample in @iter or %NULL if at the end of the list. */ IpatchSLISample * ipatch_sli_sample_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_SLI_SAMPLE(obj)); } else { return (NULL); } } /** * ipatch_sli_sample_set_name: * @sample: Sample to set name of * @name: (nullable): Value to set name to * * Sets the name of a Spectralis sample. */ void ipatch_sli_sample_set_name(IpatchSLISample *sample, const char *name) { g_return_if_fail(IPATCH_IS_SLI_SAMPLE(sample)); ipatch_sli_sample_real_set_name(sample, name, TRUE); } /* also called from item_set_property method so "name_notify" can be used to stop double emission of name notify */ static void ipatch_sli_sample_real_set_name(IpatchSLISample *sample, const char *name, gboolean name_notify) { GValue oldval = { 0 }, newval = { 0 }; char *newname; g_value_init(&oldval, G_TYPE_STRING); newname = g_strdup(name); IPATCH_ITEM_WLOCK(sample); g_value_take_string(&oldval, sample->name); sample->name = newname; IPATCH_ITEM_WUNLOCK(sample); g_value_init(&newval, G_TYPE_STRING); g_value_set_static_string(&newval, name); ipatch_item_prop_notify((IpatchItem *)sample, ipatch_item_pspec_title, &newval, &oldval); if(name_notify) { ipatch_item_prop_notify((IpatchItem *)sample, name_pspec, &newval, &oldval); } g_value_unset(&oldval); g_value_unset(&newval); } /** * ipatch_sli_sample_get_name: * @sample: Sample to get name of * * Gets the name of a Spectralis sample. * * Returns: Name of sample or %NULL if not set. String value should be freed * when finished with it. */ char * ipatch_sli_sample_get_name(IpatchSLISample *sample) { char *name = NULL; g_return_val_if_fail(IPATCH_IS_SLI_SAMPLE(sample), NULL); IPATCH_ITEM_RLOCK(sample); if(sample->name) { name = g_strdup(sample->name); } IPATCH_ITEM_RUNLOCK(sample); return (name); } /** * ipatch_sli_sample_set_data: * @sample: Sample to set sample data of * @sampledata: Sample data to set sample to * * Set a sample's sample data object. */ void ipatch_sli_sample_set_data(IpatchSLISample *sample, IpatchSampleData *sampledata) { g_return_if_fail(IPATCH_IS_SLI_SAMPLE(sample)); g_return_if_fail(IPATCH_IS_SAMPLE_DATA(sampledata)); ipatch_sli_sample_real_set_data(sample, sampledata, TRUE); } /* the actual setting of sample data, user routine does a g_object_notify */ static void ipatch_sli_sample_real_set_data(IpatchSLISample *sample, IpatchSampleData *sampledata, gboolean data_notify) { GValue oldval = { 0 }, newval = { 0 }; IpatchSampleData *old_sampledata; if(sampledata) { g_object_ref(sampledata); ipatch_sample_data_used(sampledata); /* ++ inc use count */ } IPATCH_ITEM_WLOCK(sample); old_sampledata = sample->sample_data; sample->sample_data = sampledata; IPATCH_ITEM_WUNLOCK(sample); if(old_sampledata) { ipatch_sample_data_unused(old_sampledata); // -- dec use count } if(data_notify) { g_value_init(&newval, IPATCH_TYPE_SAMPLE_DATA); g_value_set_object(&newval, sampledata); g_value_init(&oldval, IPATCH_TYPE_SAMPLE_DATA); g_value_take_object(&oldval, old_sampledata); ipatch_item_prop_notify((IpatchItem *)sample, sample_data_pspec, &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } else if(old_sampledata) { g_object_unref(old_sampledata); } } /** * ipatch_sli_sample_get_data: * @sample: Sample to get sample data from * * Get the #IpatchSampleData item of a sample. Sample data item is referenced * before returning and caller is responsible for unreferencing it with * g_object_unref() when finished with it. * * Returns: (transfer full): Sample data object of sample or %NULL if none. Remember to * unreference with g_object_unref() when finished with it. */ IpatchSampleData * ipatch_sli_sample_get_data(IpatchSLISample *sample) { IpatchSampleData *sampledata; g_return_val_if_fail(IPATCH_IS_SLI_SAMPLE(sample), NULL); IPATCH_ITEM_RLOCK(sample); sampledata = sample->sample_data; if(sampledata) { g_object_ref(sampledata); /* ++ ref sampledata */ } IPATCH_ITEM_RUNLOCK(sample); return (sampledata); /* !! ref passed to caller */ } /** * ipatch_sli_sample_peek_data: (skip) * @sample: Sample to get sample data from * * Get the #IpatchSampleData item of a sample. Like * ipatch_sli_sample_get_data() but sample data object is not referenced. * This function should only be used if a reference of the sample data object * is ensured or only the pointer value is of importance. * * Returns: Sample data object of sample or %NULL if none. Remember that a * reference is NOT added. */ IpatchSampleData * ipatch_sli_sample_peek_data(IpatchSLISample *sample) { IpatchSampleData *sampledata; g_return_val_if_fail(IPATCH_IS_SLI_SAMPLE(sample), NULL); IPATCH_ITEM_RLOCK(sample); sampledata = sample->sample_data; IPATCH_ITEM_RUNLOCK(sample); return (sampledata); } /** * ipatch_sli_sample_set_blank: * @sample: Sample to set to blank sample data * * Set the sample data of a sample item to blank data. */ void ipatch_sli_sample_set_blank(IpatchSLISample *sample) { IpatchSampleData *sampledata; g_return_if_fail(IPATCH_IS_SLI_SAMPLE(sample)); sampledata = ipatch_sample_data_get_blank(); ipatch_item_set_atomic(sample, "sample-data", sampledata, "loop-start", 8, "loop-end", 40, "root-note", 60, "fine-tune", 0, NULL); g_object_unref(sampledata); } libinstpatch-1.1.6/libinstpatch/IpatchSLISample.h000066400000000000000000000061111400263525300217610ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SLI_SAMPLE_H__ #define __IPATCH_SLI_SAMPLE_H__ #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchSLISample IpatchSLISample; typedef struct _IpatchSLISampleClass IpatchSLISampleClass; #define IPATCH_TYPE_SLI_SAMPLE (ipatch_sli_sample_get_type ()) #define IPATCH_SLI_SAMPLE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SLI_SAMPLE, \ IpatchSLISample)) #define IPATCH_SLI_SAMPLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SLI_SAMPLE, \ IpatchSLISampleClass)) #define IPATCH_IS_SLI_SAMPLE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SLI_SAMPLE)) #define IPATCH_IS_SLI_SAMPLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SLI_SAMPLE)) #define IPATCH_SLI_SAMPLE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SLI_SAMPLE, \ IpatchSLISampleClass)) /* Spectralis sample item */ struct _IpatchSLISample { IpatchItem parent_instance; IpatchSampleData *sample_data; /* sample data object */ char *name; /* name of sample */ guint32 rate; /* sample rate */ guint32 loop_start; /* loop start offset (in samples) */ guint32 loop_end; /* loop end offset (in samples) */ guint8 root_note; /* root midi note number */ gint8 fine_tune; /* fine tuning in cents */ }; struct _IpatchSLISampleClass { IpatchItemClass parent_class; }; GType ipatch_sli_sample_get_type(void); IpatchSLISample *ipatch_sli_sample_new(void); IpatchSLISample *ipatch_sli_sample_first(IpatchIter *iter); IpatchSLISample *ipatch_sli_sample_next(IpatchIter *iter); void ipatch_sli_sample_set_name(IpatchSLISample *sample, const char *name); char *ipatch_sli_sample_get_name(IpatchSLISample *sample); void ipatch_sli_sample_set_data(IpatchSLISample *sample, IpatchSampleData *sampledata); IpatchSampleData *ipatch_sli_sample_get_data(IpatchSLISample *sample); IpatchSampleData *ipatch_sli_sample_peek_data(IpatchSLISample *sample); void ipatch_sli_sample_set_blank(IpatchSLISample *sample); #endif libinstpatch-1.1.6/libinstpatch/IpatchSLIWriter.c000066400000000000000000001025371400263525300220200ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSLIWriter * @short_description: Spectralis SLI/SLC instrument file writer * @see_also: #IpatchSLI * * Writes an SLI instrument object tree (#IpatchSLI) to an SLI or SLC file. */ #include #include #include #include #include #include #include #include "IpatchSLIFile_priv.h" #include "compat.h" #include "ipatch_priv.h" #include "i18n.h" /* NOTICE: A duplicate SLI object is used for saving. This * solves all multi-thread issues and allows one to continue editing * even while a file is being saved. It also means that the * duplicate SLI object can be accessed directly without * locking. Sample data objects are not duplicated though, so they * still need to be dealt with properly. */ /* Hash value used for sample_hash */ typedef struct { guint index; /* sample index */ guint position; /* position in file */ guint offset; /* offset in chunk */ guint length; /* data length in bytes */ guint channels; /* channel count */ } SampleHashValue; #define FORMAT_16BIT IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_SIGNED | IPATCH_SAMPLE_LENDIAN static void ipatch_sli_writer_finalize(GObject *object); static GQuark ipatch_sli_writer_error_quark(void); static SampleHashValue *ipatch_sli_writer_sample_hash_value_new(void); static void ipatch_sli_writer_sample_hash_value_destroy(gpointer value); static gint ipatch_sli_writer_inst_find_func(gconstpointer a, gconstpointer b); static GSList *ipatch_sli_writer_find_groups(IpatchSLI *sli); static gboolean ipatch_sli_writer_write_group(IpatchSLIWriter *writer, GPtrArray *ig, GError **err); static gboolean ipatch_sli_writer_write_sifi(IpatchFileHandle *handle, guint ignum, GError **err); static void ipatch_sli_writer_write_siig(IpatchFileHandle *handle, IpatchSLISiIg *siig); static void ipatch_sli_writer_write_inst_header(IpatchFileHandle *handle, IpatchSLIInstHeader *ihdr); static void ipatch_sli_writer_write_zone_header(IpatchFileHandle *handle, IpatchSLIZone *zone, guint sample_idx); static void ipatch_sli_writer_write_sample_header(IpatchFileHandle *handle, SampleHashValue *sample_info, IpatchSLISample *sample); static void ipatch_sli_writer_write_sidp(IpatchFileHandle *handle, IpatchSLISiDp *sidp); static gboolean ipatch_sli_writer_write_sample_data(IpatchFileHandle *handle, IpatchSLISample *sample, GError **err); G_DEFINE_TYPE(IpatchSLIWriter, ipatch_sli_writer, G_TYPE_OBJECT) static void ipatch_sli_writer_class_init(IpatchSLIWriterClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->finalize = ipatch_sli_writer_finalize; } static void ipatch_sli_writer_init(IpatchSLIWriter *writer) { writer->sli = NULL; writer->sample_hash = g_hash_table_new_full(NULL, NULL, NULL, ipatch_sli_writer_sample_hash_value_destroy); } static void ipatch_sli_writer_finalize(GObject *object) { IpatchSLIWriter *writer = IPATCH_SLI_WRITER(object); if(writer->handle) { ipatch_file_close(writer->handle); /* -- unref file object */ } if(writer->orig_sli) { g_object_unref(writer->orig_sli); writer->orig_sli = NULL; } if(writer->sli) { g_object_unref(writer->sli); writer->sli = NULL; } if(writer->sample_hash) { g_hash_table_destroy(writer->sample_hash); writer->sample_hash = NULL; } if(writer->store_list) { g_object_unref(writer->store_list); writer->store_list = NULL; } if(G_OBJECT_CLASS(ipatch_sli_writer_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_sli_writer_parent_class)->finalize(object); } } static GQuark ipatch_sli_writer_error_quark(void) { static GQuark q = 0; if(q == 0) { q = g_quark_from_static_string("sli-writer-error-quark"); } return (q); } static SampleHashValue * ipatch_sli_writer_sample_hash_value_new(void) { return (g_slice_new0(SampleHashValue)); } static void ipatch_sli_writer_sample_hash_value_destroy(gpointer value) { g_slice_free(SampleHashValue, value); } /** * ipatch_sli_writer_new: * @handle: SLI file handle to save to or %NULL to set later, taken over by * writer object and will be closed on finalize. * @sli: SLI object to save or %NULL to set later * * Create a new SLI file writer. * * Returns: The new SLI writer */ IpatchSLIWriter * ipatch_sli_writer_new(IpatchFileHandle *handle, IpatchSLI *sli) { IpatchSLIWriter *writer; g_return_val_if_fail(!handle || IPATCH_IS_SLI_FILE(handle->file), NULL); g_return_val_if_fail(!sli || IPATCH_IS_SLI(sli), NULL); writer = g_object_new(IPATCH_TYPE_SLI_WRITER, NULL); if(handle) { ipatch_sli_writer_set_file_handle(writer, handle); } if(sli) { ipatch_sli_writer_set_patch(writer, sli); } return (writer); } /** * ipatch_sli_writer_set_patch: * @writer: SLI writer object * @sli: SLI patch to save * * Set the SLI patch object to save with a SLI writer. */ void ipatch_sli_writer_set_patch(IpatchSLIWriter *writer, IpatchSLI *sli) { g_return_if_fail(IPATCH_IS_SLI_WRITER(writer)); g_return_if_fail(IPATCH_IS_SLI(sli)); if(writer->orig_sli) { g_object_unref(writer->orig_sli); } writer->orig_sli = g_object_ref(sli); } /** * ipatch_sli_writer_set_file_handle: * @writer: SLI writer object * @handle: (nullable): SLI file handle or %NULL to clear * * Set the SLI file handle of an SLI writer. */ void ipatch_sli_writer_set_file_handle(IpatchSLIWriter *writer, IpatchFileHandle *handle) { g_return_if_fail(IPATCH_IS_SLI_WRITER(writer)); g_return_if_fail(handle && IPATCH_IS_SLI_FILE(handle->file)); /* Close old handle, if any */ if(writer->handle) { ipatch_file_close(writer->handle); } writer->handle = handle; } /** * ipatch_sli_writer_save: * @writer: SLI writer object * @err: Location to store error info or %NULL * * Write an SLI object to a file. * * Returns: %TRUE on success, %FALSE on error */ gboolean ipatch_sli_writer_save(IpatchSLIWriter *writer, GError **err) { GSList *ig, *igs = NULL; guint32 len; gboolean ret; g_return_val_if_fail(IPATCH_IS_SLI_WRITER(writer), FALSE); g_return_val_if_fail(!err || !*err, FALSE); g_return_val_if_fail(writer->handle != NULL, FALSE); g_return_val_if_fail(writer->orig_sli != NULL, FALSE); if(writer->sli) { g_object_unref(writer->sli); /* shouldn't be set, but.. */ } /* ++ref new duplicate sli object */ writer->sli = IPATCH_SLI(ipatch_item_duplicate(IPATCH_ITEM(writer->orig_sli))); g_return_val_if_fail(writer->sli != NULL, FALSE); /* build instrument groups and write main file header */ igs = ipatch_sli_writer_find_groups(writer->sli); /* ++ ref igs */ if(!igs) { g_set_error(err, ipatch_sli_writer_error_quark(), IPATCH_RIFF_ERROR_INVALID_DATA, "Could not determine groups for SLI"); return (FALSE); } if(!ipatch_sli_writer_write_sifi(writer->handle, g_slist_length(igs), err)) { ret = FALSE; goto end; } /* write groups */ for(ig = igs; ig; ig = g_slist_next(ig)) { if(!ipatch_sli_writer_write_group(writer, ig->data, err)) { ret = FALSE; goto end; } } /* fixup cklen field in SiFi header */ len = ipatch_file_get_position(writer->handle); if(!ipatch_file_seek(writer->handle, IPATCH_RIFF_FOURCC_SIZE, G_SEEK_SET, err) || !ipatch_file_write_u32(writer->handle, len, err)) { ret = FALSE; goto end; } ret = TRUE; g_object_set(writer->orig_sli, "changed", FALSE, /* file and object are in sync */ "saved", TRUE, /* has now been saved */ NULL); end: g_slist_free_full(igs, (GDestroyNotify)g_ptr_array_unref); /* -- unref igs */ /* keep duplicated sli object for create_stores unless there was an error */ if(!ret) { g_object_unref(writer->sli); /* -- unref duplicate sli object */ writer->sli = NULL; } return ret; } /** * ipatch_sli_writer_create_stores: * @writer: SLI writer object * * Create sample stores and add them to applicable #IpatchSampleData objects and return object list. * This function can be called multiple times, additional calls will return the same list. * * Returns: (transfer full): List of sample stores which the caller owns a reference to or %NULL */ IpatchList * ipatch_sli_writer_create_stores(IpatchSLIWriter *writer) { SampleHashValue *hash_value; IpatchSample *newstore; IpatchSLISample *sample; IpatchIter iter; IpatchList *list; int rate, format; guint size; g_return_val_if_fail(writer->sli != NULL, NULL); /* Return existing store list (if this function has been called before) */ if(writer->store_list) { return (g_object_ref(writer->store_list)); /* ++ ref for caller */ } if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sli), &iter, IPATCH_TYPE_SLI_SAMPLE)) { return (NULL); } list = ipatch_list_new(); /* ++ ref list */ /* traverse samples */ for(sample = ipatch_sli_sample_first(&iter); sample; sample = ipatch_sli_sample_next(&iter)) { hash_value = g_hash_table_lookup(writer->sample_hash, sample); /* hash_value should never be NULL, but.. */ if(!hash_value) { continue; } g_object_get(sample, "sample-format", &format, "sample-size", &size, "sample-rate", &rate, NULL); /* ++ ref newstore */ newstore = ipatch_sample_store_file_new(writer->handle->file, hash_value->position); format &= IPATCH_SAMPLE_CHANNEL_MASK; format |= FORMAT_16BIT; g_object_set(newstore, "sample-format", format, "sample-size", size, "sample-rate", rate, NULL); ipatch_sample_data_add(sample->sample_data, (IpatchSampleStore *)newstore); /* !! list takes over reference */ list->items = g_list_prepend(list->items, newstore); } writer->store_list = g_object_ref(list); /* ++ ref for writer object */ return (list); /* !! caller takes over reference */ } static gint ipatch_sli_writer_inst_find_func(gconstpointer a, gconstpointer b) { const GPtrArray *arr = a; guint i; for(i = 0; i < arr->len; i++) if(g_ptr_array_index(arr, i) == b) { return 0; } return -1; } static GSList * ipatch_sli_writer_find_groups(IpatchSLI *sli) { IpatchIter inst_iter, iz_iter; IpatchItem *item; GSList *ig, *igs = NULL; GPtrArray *insts; IpatchList *inst_zones, *zlist; IpatchSLIZone *iz, *z; if(!ipatch_container_init_iter((IpatchContainer *)sli, &inst_iter, IPATCH_TYPE_SLI_INST)) { return (NULL); } for(item = ipatch_iter_first(&inst_iter); item; item = ipatch_iter_next(&inst_iter)) { ig = g_slist_find_custom(igs, item, ipatch_sli_writer_inst_find_func); if(ig) /* found in a group */ { insts = ig->data; } else /* if not already in a group then we create a new one */ { insts = g_ptr_array_new(); g_ptr_array_add(insts, item); igs = g_slist_prepend(igs, insts); } inst_zones = ipatch_sli_inst_get_zones(item); /* ++ ref inst_zones */ ipatch_list_init_iter(inst_zones, &iz_iter); for(iz = ipatch_sli_zone_first(&iz_iter); iz; iz = ipatch_sli_zone_next(&iz_iter)) { IpatchIter iter; IpatchItem *inst; /* ++ ref zlist (list of zones also referencing sample of this zone) */ zlist = ipatch_sli_get_zone_references(ipatch_sli_zone_peek_sample(iz)); ipatch_list_init_iter(zlist, &iter); for(z = ipatch_sli_zone_first(&iter); z; z = ipatch_sli_zone_next(&iter)) { inst = ipatch_item_peek_parent(IPATCH_ITEM(z)); ig = g_slist_find_custom(igs, inst, ipatch_sli_writer_inst_find_func); if(!ig) /* not yet in any group: add to current */ { g_ptr_array_add(insts, inst); } else if(ig->data != insts) /* already in another group: merge groups */ { GPtrArray *igrp = ig->data; int i; for(i = igrp->len - 1; i >= 0; i--) { g_ptr_array_add(insts, g_ptr_array_index(igrp, i)); g_ptr_array_remove_index_fast(igrp, i); } g_ptr_array_free(igrp, TRUE); igs = g_slist_delete_link(igs, ig); } } g_object_unref(zlist); /* -- unref zlist */ } g_object_unref(inst_zones); /* -- unref inst_zones */ } igs = g_slist_reverse(igs); /* fixup for prepending to preserve order */ return (igs); } static gboolean ipatch_sli_writer_write_group(IpatchSLIWriter *writer, GPtrArray *ig, GError **err) { guint i, cnt, allzones = 0, maxzones = 0, smpdata_size = 0; IpatchList *inst_zones; IpatchIter iz_iter; IpatchSLIInst *inst; IpatchSLIZone *zone; IpatchSLISample *sample = NULL; SampleHashValue *sample_info; GPtrArray *samples; IpatchSLISiIg siig; IpatchSLIInstHeader ihdr; IpatchSLISiDp sidp = { IPATCH_SLI_FOURCC_SIDP, IPATCH_SLI_SIDP_SIZE, IPATCH_SLI_SPECHDR_VAL, 0 }; guint pos; pos = ipatch_file_get_position(writer->handle); samples = g_ptr_array_new(); /* prepare buffer for headers */ ipatch_file_buf_zero(writer->handle, IPATCH_SLI_HEAD_SIZE); /* loop over instruments in group */ for(i = 0; i < ig->len; i++) { inst = g_ptr_array_index(ig, i); inst_zones = ipatch_sli_inst_get_zones(inst); /* ++ ref inst_zones */ ipatch_list_init_iter(inst_zones, &iz_iter); cnt = ipatch_iter_count(&iz_iter); /* write instrument header */ strncpy(ihdr.name, inst->name, IPATCH_SLI_NAME_SIZE); ihdr.sound_id = (inst->sound_id ? inst->sound_id : g_str_hash(inst->name)); ihdr.unused1 = 0; ihdr.category = inst->category; ihdr.unused2 = 0; ihdr.zone_idx = allzones; ihdr.zones_num = cnt; if(cnt > maxzones) { maxzones = cnt; } allzones += cnt; ipatch_file_buf_seek(writer->handle, IPATCH_SLI_SIIG_SIZE + i * IPATCH_SLI_INST_SIZE, G_SEEK_SET); ipatch_sli_writer_write_inst_header(writer->handle, &ihdr); /* loop over zones of this instrument */ ipatch_file_buf_seek(writer->handle, IPATCH_SLI_SIIG_SIZE + ig->len * IPATCH_SLI_INST_SIZE + ihdr.zone_idx * IPATCH_SLI_ZONE_SIZE, G_SEEK_SET); for(zone = ipatch_sli_zone_first(&iz_iter); zone; zone = ipatch_sli_zone_next(&iz_iter)) { int format; /* write zone header */ sample = ipatch_sli_zone_peek_sample(zone); sample_info = g_hash_table_lookup(writer->sample_hash, sample); ipatch_sli_writer_write_zone_header(writer->handle, zone, (sample_info ? sample_info->index : samples->len)); /* check referenced sample: if already counted then continue */ if(sample_info) { continue; } /* else check sample format and add info to sample hash */ format = ipatch_sample_get_format((IpatchSample *)sample); if(IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format) > 2) { char *n = ipatch_sli_sample_get_name(sample); g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNSUPPORTED, _("Unsupported channel count in sample '%s'"), n); if(n) { free(n); } g_object_unref(inst_zones); /* -- unref inst_zones */ return (FALSE); } format &= IPATCH_SAMPLE_CHANNEL_MASK; format |= FORMAT_16BIT; sample_info = ipatch_sli_writer_sample_hash_value_new(); sample_info->index = samples->len; sample_info->channels = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format); g_hash_table_insert(writer->sample_hash, sample, sample_info); g_ptr_array_add(samples, sample); sample_info->offset = smpdata_size; sample_info->position = pos + sample_info->offset; sample_info->length = ipatch_sample_get_size(IPATCH_SAMPLE(sample), NULL) * ipatch_sample_format_size(format); /* plus 64 zero bytes written for each channel*/ smpdata_size += sample_info->length + sample_info->channels * 64; } g_object_unref(inst_zones); /* -- unref inst_zones */ } /* now rewind to the beginning and write group header */ ipatch_file_buf_seek(writer->handle, 0, G_SEEK_SET); siig.ckid = IPATCH_SLI_FOURCC_SIIG; siig.cklen = IPATCH_SLI_SIIG_SIZE; /* group header */ siig.cklen += ig->len * IPATCH_SLI_INST_SIZE; /* instrument headers */ siig.cklen += allzones * IPATCH_SLI_ZONE_SIZE; /* zone headers */ siig.cklen += samples->len * IPATCH_SLI_SMPL_SIZE; /* sample headers */ if(siig.cklen >= IPATCH_SLI_HEAD_SIZE) { g_set_error(err, IPATCH_ERROR, IPATCH_RIFF_ERROR_SIZE_EXCEEDED, _("Too many instruments, zones or samples. Header size exceeded.")); return (FALSE); } siig.cklen += smpdata_size; /* sample data */ siig.spechdr = IPATCH_SLI_SPECHDR_VAL; siig.unused1 = 0; siig.inst_offs = IPATCH_SLI_SIIG_SIZE; siig.instnum = ig->len; siig.zones_offs = siig.inst_offs + siig.instnum * IPATCH_SLI_INST_SIZE; siig.allzones_num = allzones; siig.smphdr_offs = siig.zones_offs + siig.allzones_num * IPATCH_SLI_ZONE_SIZE; siig.maxzones_num = maxzones; siig.smpdata_offs = siig.smphdr_offs + samples->len * IPATCH_SLI_SMPL_SIZE; siig.unused2 = 0; ipatch_sli_writer_write_siig(writer->handle, &siig); /* write sample headers */ ipatch_file_buf_seek(writer->handle, siig.smphdr_offs, G_SEEK_SET); for(i = 0; i < samples->len; i++) { sample_info = g_hash_table_lookup(writer->sample_hash, sample); sample_info->position += siig.smphdr_offs; ipatch_sli_writer_write_sample_header(writer->handle, sample_info, g_ptr_array_index(samples, i)); } /* finished assembling headers, commit to the file now */ ipatch_file_buf_set_size(writer->handle, ipatch_file_get_position(writer->handle) - pos); if(!ipatch_file_buf_commit(writer->handle, err)) { return FALSE; } /* write sample data */ /* write sample headers */ for(i = 0; i < samples->len; i++) if(!ipatch_sli_writer_write_sample_data(writer->handle, g_ptr_array_index(samples, i), err)) { return FALSE; } /* write SiDp headers (one for each instrument) */ for(i = 0; i < ig->len; i++) { ipatch_sli_writer_write_sidp(writer->handle, &sidp); } if(!ipatch_file_buf_commit(writer->handle, err)) { return FALSE; } return TRUE; } static gboolean ipatch_sli_writer_write_sifi(IpatchFileHandle *handle, guint ignum, GError **err) { guint32 header[2] = {IPATCH_SLI_FOURCC_SIFI, 0}; /* Header is written without possible swapping for endianness * Fixup cklen later after writing whole file */ ipatch_file_buf_write(handle, header, 8); ipatch_file_buf_write_u16(handle, IPATCH_SLI_SPECHDR_VAL); ipatch_file_buf_write_u16(handle, 0); ipatch_file_buf_write_u16(handle, ignum); ipatch_file_buf_write_u16(handle, IPATCH_RIFF_HEADER_SIZE + IPATCH_SLI_SIFI_SIZE); return ipatch_file_buf_commit(handle, err); } static void ipatch_sli_writer_write_siig(IpatchFileHandle *handle, IpatchSLISiIg *siig) { /* id is written without possible swapping for endianness */ ipatch_file_buf_write(handle, &(siig->ckid), 4); ipatch_file_buf_write_u32(handle, siig->cklen); ipatch_file_buf_write_u16(handle, siig->spechdr); ipatch_file_buf_write_u16(handle, siig->unused1); ipatch_file_buf_write_u16(handle, siig->inst_offs); ipatch_file_buf_write_u16(handle, siig->instnum); ipatch_file_buf_write_u16(handle, siig->zones_offs); ipatch_file_buf_write_u16(handle, siig->allzones_num); ipatch_file_buf_write_u16(handle, siig->smphdr_offs); ipatch_file_buf_write_u16(handle, siig->maxzones_num); ipatch_file_buf_write_u16(handle, siig->smpdata_offs); ipatch_file_buf_write_u16(handle, siig->unused2); } static void ipatch_sli_writer_write_inst_header(IpatchFileHandle *handle, IpatchSLIInstHeader *ihdr) { ipatch_file_buf_write(handle, ihdr->name, IPATCH_SLI_NAME_SIZE); ipatch_file_buf_write_u32(handle, ihdr->sound_id); ipatch_file_buf_write_u32(handle, ihdr->unused1); ipatch_file_buf_write_u16(handle, ihdr->category); ipatch_file_buf_write_u16(handle, ihdr->unused2); ipatch_file_buf_write_u16(handle, ihdr->zone_idx); ipatch_file_buf_write_u16(handle, ihdr->zones_num); } static void ipatch_sli_writer_write_zone_header(IpatchFileHandle *handle, IpatchSLIZone *zone, guint sample_idx) { IpatchSF2GenItem *item; IpatchSF2GenAmount amount; guint32 offs; item = IPATCH_SF2_GEN_ITEM(zone); ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_NOTE_RANGE, &amount); ipatch_file_buf_write_u8(handle, amount.range.low); /* keyrange_low */ ipatch_file_buf_write_u8(handle, amount.range.high); /* keyrange_high */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VELOCITY_RANGE, &amount); ipatch_file_buf_write_u8(handle, amount.range.low); /* velrange_low */ ipatch_file_buf_write_u8(handle, amount.range.high); /* velrange_high */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_SAMPLE_COARSE_START, &amount); offs = amount.uword << 16; ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_SAMPLE_START, &amount); offs += amount.uword << 1; ipatch_file_buf_write_u32(handle, offs); /* start_offs1 */ ipatch_file_buf_write_u32(handle, offs); /* start_offs2 */ ipatch_file_buf_write_u32(handle, 0); /* unknown1 */ ipatch_file_buf_write_u32(handle, 0); /* unknown2 */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_COARSE_TUNE, &amount); ipatch_file_buf_write_s8(handle, (guint8)amount.sword); /* coarse_tune1 */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE, &amount); ipatch_file_buf_write_s8(handle, (guint8)amount.sword); /* fine_tune1 */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_SAMPLE_MODES, &amount); if(amount.uword & IPATCH_SF2_GEN_SAMPLE_MODE_LOOP) { zone->flags |= IPATCH_SF2_GEN_SAMPLE_MODE_LOOP; } ipatch_file_buf_write_u8(handle, zone->flags); /* sample_modes */ if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE, &amount)) { amount.sword = 0; } ipatch_file_buf_write_s8(handle, (guint8)amount.sword); /* root_note */ if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_SCALE_TUNE, &amount)) { amount.uword = 0; } ipatch_file_buf_write_u16(handle, amount.uword); /* scale_tuning */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_COARSE_TUNE, &amount); ipatch_file_buf_write_s8(handle, (guint8)amount.sword); /* coarse_tune2 */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE, &amount); ipatch_file_buf_write_s8(handle, (guint8)amount.sword); /* fine_tune2 */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_LFO_TO_PITCH, &amount); ipatch_file_buf_write_s16(handle, amount.sword); /* modLfoToPitch */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VIB_LFO_TO_PITCH, &amount); ipatch_file_buf_write_s16(handle, amount.sword); /* vibLfoToPitch */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_ENV_TO_PITCH, &amount); ipatch_file_buf_write_s16(handle, amount.sword); /* modEnvToPitch */ if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_FILTER_CUTOFF, &amount)) { amount.uword = 0; } ipatch_file_buf_write_u16(handle, amount.uword); /* initialFilterFc */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_FILTER_Q, &amount); ipatch_file_buf_write_u16(handle, amount.uword); /* initialFilterQ */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_LFO_TO_FILTER_CUTOFF, &amount); ipatch_file_buf_write_s16(handle, amount.sword); /* modLfoToFilterFc */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_ENV_TO_FILTER_CUTOFF, &amount); ipatch_file_buf_write_s16(handle, amount.sword); /* modEnvToFilterFc */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_LFO_TO_VOLUME, &amount); ipatch_file_buf_write_s16(handle, amount.sword); /* modLfoToVolume */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_LFO_FREQ, &amount); ipatch_file_buf_write_s16(handle, amount.sword); /* freqModLfo */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VIB_LFO_FREQ, &amount); ipatch_file_buf_write_s16(handle, amount.sword); /* freqVibLfo */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_ENV_SUSTAIN, &amount); ipatch_file_buf_write_u16(handle, amount.uword); /* sustainModEnv */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_HOLD, &amount); ipatch_file_buf_write_s16(handle, amount.sword); /* keynumToModEnvHold */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_DECAY, &amount); ipatch_file_buf_write_s16(handle, amount.sword); /* keynumToModEnvDecay */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VOL_ENV_SUSTAIN, &amount); ipatch_file_buf_write_u16(handle, amount.uword); /* sustainVolEnv */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_HOLD, &amount); ipatch_file_buf_write_s16(handle, amount.sword); /* keynumToVolEnvHold */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_DECAY, &amount); ipatch_file_buf_write_s16(handle, amount.sword); /* keynumToVolEnvDecay */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_PAN, &amount); ipatch_file_buf_write_s8(handle, amount.sword / 5); /* pan */ if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_LFO_DELAY, &amount)) { amount.sword = 0; } ipatch_file_buf_write_s8(handle, amount.sword / 100); /* delayModLfo */ if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VIB_LFO_DELAY, &amount)) { amount.sword = 0; } ipatch_file_buf_write_s8(handle, amount.sword / 100); /* delayVibLfo */ if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_ENV_ATTACK, &amount)) { amount.sword = 0; } ipatch_file_buf_write_s8(handle, amount.sword / 100); /* attackModEnv */ if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_ENV_HOLD, &amount)) { amount.sword = 0; } ipatch_file_buf_write_s8(handle, amount.sword / 100); /* holdModEnv */ if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_ENV_DECAY, &amount)) { amount.sword = 0; } ipatch_file_buf_write_s8(handle, amount.sword / 100); /* decayModEnv */ if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_ENV_RELEASE, &amount)) { amount.sword = 0; } ipatch_file_buf_write_s8(handle, amount.sword / 100); /* releaseModEnv */ if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VOL_ENV_ATTACK, &amount)) { amount.sword = 0; } ipatch_file_buf_write_s8(handle, amount.sword / 100); /* attackVolEnv */ if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VOL_ENV_HOLD, &amount)) { amount.sword = 0; } ipatch_file_buf_write_s8(handle, amount.sword / 100); /* holdVolEnv */ if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VOL_ENV_DECAY, &amount)) { amount.sword = 0; } ipatch_file_buf_write_s8(handle, amount.sword / 100); /* decayVolEnv */ if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VOL_ENV_RELEASE, &amount)) { amount.sword = 0; } ipatch_file_buf_write_s8(handle, amount.sword / 100); /* releaseVolEnv */ ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_ATTENUATION, &amount); ipatch_file_buf_write_u8(handle, amount.uword / 10); /* initialAttenuation */ ipatch_file_buf_write_u16(handle, sample_idx); /* sample_idx */ ipatch_file_buf_write_u16(handle, 0); /* unused */ } static void ipatch_sli_writer_write_sample_header(IpatchFileHandle *handle, SampleHashValue *sample_info, IpatchSLISample *sample) { char sname[IPATCH_SLI_NAME_SIZE]; strncpy(sname, sample->name, IPATCH_SLI_NAME_SIZE); ipatch_file_buf_write(handle, sname, IPATCH_SLI_NAME_SIZE); ipatch_file_buf_write_u32(handle, sample_info->offset); ipatch_file_buf_write_u32(handle, sample_info->offset + sample_info->length); ipatch_file_buf_write_u32(handle, sample->loop_start * 2); ipatch_file_buf_write_u32(handle, sample->loop_end * 2); ipatch_file_buf_write_s8(handle, sample->fine_tune); ipatch_file_buf_write_u8(handle, sample->root_note); ipatch_file_buf_write_u8(handle, sample_info->channels); ipatch_file_buf_write_u8(handle, 16); /* bits per sample is verified above */ ipatch_file_buf_write_u32(handle, sample->rate); } static void ipatch_sli_writer_write_sidp(IpatchFileHandle *handle, IpatchSLISiDp *sidp) { /* id is written without possible swapping for endianness */ ipatch_file_buf_write(handle, &(sidp->ckid), 4); ipatch_file_buf_write_u32(handle, sidp->cklen); ipatch_file_buf_write_u16(handle, sidp->spechdr); ipatch_file_buf_write_u16(handle, sidp->unused); } static gboolean ipatch_sli_writer_write_sample_data(IpatchFileHandle *handle, IpatchSLISample *sample, GError **err) { IpatchSampleHandle shandle; gpointer buf; guint samsize, fmt_size, read_size, offs; int format; samsize = ipatch_sample_get_size(IPATCH_SAMPLE(sample->sample_data), NULL); format = ipatch_sample_get_format(IPATCH_SAMPLE(sample->sample_data)); format &= IPATCH_SAMPLE_CHANNEL_MASK; format |= FORMAT_16BIT; fmt_size = ipatch_sample_format_size(format); /* ++ open sample handle */ if(!ipatch_sample_data_open_native_sample(sample->sample_data, &shandle, 'r', format, IPATCH_SAMPLE_UNITY_CHANNEL_MAP, err)) { return FALSE; } read_size = ipatch_sample_handle_get_max_frames(&shandle); for(offs = 0; offs < samsize; offs += read_size) { if(samsize - offs < read_size) /* check for last partial fragment */ { read_size = samsize - offs; } /* read and transform (if necessary) audio data from sample store */ if(!(buf = ipatch_sample_handle_read(&shandle, offs, read_size, NULL, err)) || !ipatch_file_write(handle, buf, read_size * fmt_size, err)) { ipatch_sample_handle_close(&shandle); /* -- close sample handle */ return FALSE; } } ipatch_sample_handle_close(&shandle); /* -- close sample handle */ /* write 64 "zero" samples (for each channel) following sample data */ ipatch_file_buf_zero(handle, 64 * IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format)); return ipatch_file_buf_commit(handle, err); } libinstpatch-1.1.6/libinstpatch/IpatchSLIWriter.h000066400000000000000000000055341400263525300220240ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SLI_WRITER_H__ #define __IPATCH_SLI_WRITER_H__ #include #include #include #include typedef struct _IpatchSLIWriter IpatchSLIWriter; /* private structure */ typedef struct _IpatchSLIWriterClass IpatchSLIWriterClass; #define IPATCH_TYPE_SLI_WRITER (ipatch_sli_writer_get_type ()) #define IPATCH_SLI_WRITER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SLI_WRITER, \ IpatchSLIWriter)) #define IPATCH_SLI_WRITER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SLI_WRITER, \ IpatchSLIWriterClass)) #define IPATCH_IS_SLI_WRITER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SLI_WRITER)) #define IPATCH_IS_SLI_WRITER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SLI_WRITER)) /* Spectralis SLI/SLC writer object * Spectralis files do not sufficiently follow RIFF format * so this object is not derived from IpatchRiff class */ struct _IpatchSLIWriter { GObject parent_instance; /* derived from GObject */ IpatchFileHandle *handle; /* file object being written */ IpatchSLI *orig_sli; /* original SLI object */ IpatchSLI *sli; /* duplicated SLI object to save */ GHashTable *sample_hash; /* sample => SampleHashValue hash */ IpatchList *store_list; /* list of stores, only set if ipatch_sli_writer_get_stores() was called */ }; /* Spectralis SLI/SLC writer class */ struct _IpatchSLIWriterClass { GObjectClass parent_class; }; GType ipatch_sli_writer_get_type(void); IpatchSLIWriter *ipatch_sli_writer_new(IpatchFileHandle *handle, IpatchSLI *sli); void ipatch_sli_writer_set_patch(IpatchSLIWriter *writer, IpatchSLI *sli); void ipatch_sli_writer_set_file_handle(IpatchSLIWriter *writer, IpatchFileHandle *handle); gboolean ipatch_sli_writer_save(IpatchSLIWriter *writer, GError **err); IpatchList *ipatch_sli_writer_create_stores(IpatchSLIWriter *writer); #endif libinstpatch-1.1.6/libinstpatch/IpatchSLIZone.c000066400000000000000000000620041400263525300214510ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSLIZone * @short_description: Spectralis instrument zone object * @see_also: * * Zones are children of #IpatchSLIInst and define how * their referenced #IpatchSLISample is synthesized. */ #include #include #include #include #include #include "IpatchSLIZone.h" #include "IpatchSF2GenItem.h" #include "IpatchSample.h" #include "IpatchContainer.h" #include "IpatchTypeProp.h" #include "IpatchRange.h" #include "builtin_enums.h" #include "ipatch_priv.h" static void ipatch_sli_zone_sample_iface_init(IpatchSampleIface *sample_iface); static gboolean ipatch_sli_zone_sample_iface_open(IpatchSampleHandle *handle, GError **err); static void ipatch_sli_zone_gen_item_iface_init(IpatchSF2GenItemIface *genitem_iface); static void ipatch_sli_zone_class_init(IpatchSLIZoneClass *klass); static inline void ipatch_sli_zone_get_root_note(IpatchSLIZone *zone, GValue *value); static inline void ipatch_sli_zone_get_fine_tune(IpatchSLIZone *zone, GValue *value); static void ipatch_sli_zone_finalize(GObject *gobject); static void ipatch_sli_zone_get_title(IpatchSLIZone *zone, GValue *value); static void ipatch_sli_zone_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sli_zone_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sli_zone_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static void ipatch_sli_zone_item_remove_full(IpatchItem *item, gboolean full); static void ipatch_sli_zone_real_set_sample(IpatchSLIZone *zone, IpatchSLISample *sample, gboolean sample_notify); /* generator property IDs go before these */ enum { PROP_TITLE = IPATCH_SF2_GEN_ITEM_FIRST_PROP_USER_ID, PROP_LINK_ITEM, PROP_SAMPLE_SIZE, PROP_SAMPLE_FORMAT, PROP_SAMPLE_RATE, PROP_SAMPLE_DATA, PROP_LOOP_TYPE, PROP_LOOP_START, PROP_LOOP_END, PROP_ROOT_NOTE, PROP_FINE_TUNE }; /* For quicker access without lookup */ static GParamSpec *link_item_pspec; static GParamSpec *root_note_pspec; static GParamSpec *fine_tune_pspec; /* For passing data from class init to gen item interface init */ static GParamSpec **gen_item_specs = NULL; static GParamSpec **gen_item_setspecs = NULL; G_DEFINE_TYPE_WITH_CODE(IpatchSLIZone, ipatch_sli_zone, IPATCH_TYPE_ITEM, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_sli_zone_sample_iface_init) G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SF2_GEN_ITEM, ipatch_sli_zone_gen_item_iface_init)) /* sample interface initialization */ static void ipatch_sli_zone_sample_iface_init(IpatchSampleIface *sample_iface) { sample_iface->open = ipatch_sli_zone_sample_iface_open; sample_iface->loop_types = ipatch_sample_loop_types_standard; } static gboolean ipatch_sli_zone_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchSLIZone *zone = IPATCH_SLI_ZONE(handle->sample); IpatchSLISample *sample; gboolean retval; sample = ipatch_sli_zone_get_sample(zone); /* ++ ref sample */ g_return_val_if_fail(sample != NULL, FALSE); retval = ipatch_sample_handle_cascade_open(handle, IPATCH_SAMPLE(sample), err); g_object_unref(sample); /* -- unref sample */ return (retval); } /* gen item interface initialization */ static void ipatch_sli_zone_gen_item_iface_init(IpatchSF2GenItemIface *genitem_iface) { genitem_iface->genarray_ofs = G_STRUCT_OFFSET(IpatchSLIZone, genarray); genitem_iface->propstype = IPATCH_SF2_GEN_PROPS_INST; g_return_if_fail(gen_item_specs != NULL); g_return_if_fail(gen_item_setspecs != NULL); memcpy(&genitem_iface->specs, gen_item_specs, sizeof(genitem_iface->specs)); memcpy(&genitem_iface->setspecs, gen_item_setspecs, sizeof(genitem_iface->setspecs)); g_free(gen_item_specs); g_free(gen_item_setspecs); } static void ipatch_sli_zone_class_init(IpatchSLIZoneClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->finalize = ipatch_sli_zone_finalize; obj_class->get_property = ipatch_sli_zone_get_property; item_class->copy = ipatch_sli_zone_item_copy; item_class->item_set_property = ipatch_sli_zone_set_property; item_class->remove_full = ipatch_sli_zone_item_remove_full; g_object_class_override_property(obj_class, PROP_TITLE, "title"); link_item_pspec = g_param_spec_object("link-item", _("Link item"), _("Link item"), IPATCH_TYPE_SLI_SAMPLE, G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_LINK_ITEM, link_item_pspec); /* properties defined by IpatchSample interface */ ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_SIZE, "sample-size"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_FORMAT, "sample-format"); ipatch_sample_install_property(obj_class, PROP_SAMPLE_RATE, "sample-rate"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_DATA, "sample-data"); ipatch_sample_install_property(obj_class, PROP_LOOP_TYPE, "loop-type"); ipatch_sample_install_property(obj_class, PROP_LOOP_START, "loop-start"); ipatch_sample_install_property(obj_class, PROP_LOOP_END, "loop-end"); root_note_pspec = ipatch_sample_install_property(obj_class, PROP_ROOT_NOTE, "root-note"); fine_tune_pspec = ipatch_sample_install_property(obj_class, PROP_FINE_TUNE, "fine-tune"); /* install generator properties */ ipatch_sf2_gen_item_iface_install_properties(obj_class, IPATCH_SF2_GEN_PROPS_INST, &gen_item_specs, &gen_item_setspecs); } static void ipatch_sli_zone_init(IpatchSLIZone *zone) { ipatch_sf2_gen_array_init(&zone->genarray, FALSE, FALSE); } static void ipatch_sli_zone_finalize(GObject *gobject) { IpatchSLIZone *zone = IPATCH_SLI_ZONE(gobject); IPATCH_ITEM_WLOCK(zone); if(zone->sample) { g_object_unref(zone->sample); zone->sample = NULL; } IPATCH_ITEM_WUNLOCK(zone); if(G_OBJECT_CLASS(ipatch_sli_zone_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_sli_zone_parent_class)->finalize(gobject); } } static inline void ipatch_sli_zone_get_root_note(IpatchSLIZone *zone, GValue *value) { IpatchSF2GenAmount amount; IpatchSLISample *sample; int val = 0; /* root note override not set or -1? - Get sample root note value. */ if(!ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(zone), IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE, &amount) || amount.sword == -1) { /* root note override not set, get from sample */ sample = ipatch_sli_zone_get_sample(zone); /* ++ ref sample */ if(sample) { g_object_get(sample, "root-note", &val, NULL); g_object_unref(sample); /* -- unref sample */ } } else { val = amount.uword; } g_value_set_int(value, val); } static inline void ipatch_sli_zone_get_fine_tune(IpatchSLIZone *zone, GValue *value) { IpatchSF2GenAmount amount; IpatchSLISample *sample; int val = 0; /* fine tune override set? */ if(!ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(zone), IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE, &amount)) { /* fine tune override not set, get from sample */ sample = ipatch_sli_zone_get_sample(zone); /* ++ ref sample */ if(sample) { g_object_get(sample, "fine-tune", &val, NULL); g_object_unref(sample); /* -- unref sample */ } } else { val = amount.sword; } g_value_set_int(value, val); } static void ipatch_sli_zone_get_title(IpatchSLIZone *zone, GValue *value) { IpatchSLISample *sample; char *s = NULL; sample = ipatch_sli_zone_get_sample(zone); /* ++ ref sample */ if(sample) { g_object_get(sample, "name", &s, NULL); g_object_unref(sample); /* -- unref sample */ } g_value_take_string(value, s); } static void ipatch_sli_zone_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSLIZone *zone = IPATCH_SLI_ZONE(object); IpatchSLISample *sample; IpatchSF2GenAmount amount; GValue vals[2]; /* Gets zeroed below */ guint genid; guint uval; int val = 0; /* "root-note" and "fine-tune" sample properties get updated for Zone * override property or -set property */ if(property_id >= IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID) { genid = property_id - IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID; } else { genid = property_id - IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID; } if(genid == IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE) { memset(vals, 0, sizeof(vals)); g_value_init(&vals[0], G_TYPE_INT); ipatch_sli_zone_get_root_note(zone, &vals[0]); } else if(genid == IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE) { memset(vals, 0, sizeof(vals)); g_value_init(&vals[0], G_TYPE_INT); ipatch_sli_zone_get_fine_tune(zone, &vals[0]); } if(ipatch_sf2_gen_item_iface_set_property((IpatchSF2GenItem *)object, property_id, value)) { if(genid == IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE) { g_value_init(&vals[1], G_TYPE_INT); ipatch_sli_zone_get_root_note(zone, &vals[1]); ipatch_item_prop_notify((IpatchItem *)object, root_note_pspec, &vals[1], &vals[0]); } else if(genid == IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE) { g_value_init(&vals[1], G_TYPE_INT); ipatch_sli_zone_get_fine_tune(zone, &vals[1]); ipatch_item_prop_notify((IpatchItem *)object, fine_tune_pspec, &vals[1], &vals[0]); } } else { switch(property_id) { case PROP_LINK_ITEM: ipatch_sli_zone_real_set_sample(zone, IPATCH_SLI_SAMPLE (g_value_get_object(value)), FALSE); break; case PROP_SAMPLE_RATE: sample = ipatch_sli_zone_get_sample(zone); /* ++ ref sample */ if(sample) { g_object_set(sample, "sample-rate", g_value_get_int(value), NULL); g_object_unref(sample); /* -- unref sample */ } break; case PROP_LOOP_TYPE: val = g_value_get_enum(value); if(val == IPATCH_SAMPLE_LOOP_NONE) { amount.uword = IPATCH_SF2_GEN_SAMPLE_MODE_NOLOOP; } else { amount.uword = IPATCH_SF2_GEN_SAMPLE_MODE_LOOP; } ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(zone), IPATCH_SF2_GEN_SAMPLE_MODES, &amount); break; case PROP_LOOP_START: sample = ipatch_sli_zone_get_sample(zone); /* ++ ref sample */ if(sample) { g_object_get(sample, "loop-start", &uval, NULL); val = g_value_get_uint(value) - uval; /* loop start offset */ g_object_unref(sample); /* -- unref sample */ if(val >= 0) { amount.sword = val >> 15; } else { amount.sword = -(-val >> 15); } ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(zone), IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START, &amount); if(val >= 0) { amount.sword = val & 0x7FFF; } else { amount.sword = -(-val & 0x7FFF); } ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(zone), IPATCH_SF2_GEN_SAMPLE_LOOP_START, &amount); } break; case PROP_LOOP_END: sample = ipatch_sli_zone_get_sample(zone); /* ++ ref sample */ if(sample) { g_object_get(sample, "loop-end", &uval, NULL); val = g_value_get_uint(value) - uval; /* loop end offset */ g_object_unref(sample); /* -- unref sample */ if(val >= 0) { amount.sword = val >> 15; } else { amount.sword = -(-val >> 15); } ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(zone), IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END, &amount); if(val >= 0) { amount.sword = val & 0x7FFF; } else { amount.sword = -(-val & 0x7FFF); } ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(zone), IPATCH_SF2_GEN_SAMPLE_LOOP_END, &amount); } break; case PROP_ROOT_NOTE: amount.uword = g_value_get_int(value); ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(zone), IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE, &amount); break; case PROP_FINE_TUNE: amount.sword = g_value_get_int(value); ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(zone), IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE, &amount); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } } static void ipatch_sli_zone_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSLIZone *zone = IPATCH_SLI_ZONE(object); IpatchSLISample *sample; IpatchSF2GenAmount amount; guint uval = 0; int val = 0; if(!ipatch_sf2_gen_item_iface_get_property((IpatchSF2GenItem *)object, property_id, value)) { switch(property_id) { case PROP_TITLE: ipatch_sli_zone_get_title(zone, value); break; case PROP_LINK_ITEM: g_value_take_object(value, ipatch_sli_zone_get_sample(zone)); break; case PROP_SAMPLE_SIZE: sample = ipatch_sli_zone_get_sample(zone); /* ++ ref sample */ if(sample) { g_object_get_property((GObject *)sample, "sample-size", value); g_object_unref(sample); /* -- unref sample */ } break; case PROP_SAMPLE_FORMAT: sample = ipatch_sli_zone_get_sample(zone); /* ++ ref sample */ if(sample) { g_object_get_property((GObject *)sample, "sample-format", value); g_object_unref(sample); /* -- unref sample */ } break; case PROP_SAMPLE_RATE: sample = ipatch_sli_zone_get_sample(zone); /* ++ ref sample */ if(sample) { g_object_get_property((GObject *)sample, "sample-rate", value); g_object_unref(sample); /* -- unref sample */ } break; case PROP_SAMPLE_DATA: sample = ipatch_sli_zone_get_sample(zone); /* ++ ref sample */ if(sample) { g_object_get_property((GObject *)sample, "sample-data", value); g_object_unref(sample); /* -- unref sample */ } break; case PROP_LOOP_TYPE: ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(zone), IPATCH_SF2_GEN_SAMPLE_MODES, &amount); if(amount.uword == IPATCH_SF2_GEN_SAMPLE_MODE_NOLOOP) { val = IPATCH_SAMPLE_LOOP_NONE; } else { val = IPATCH_SAMPLE_LOOP_STANDARD; } g_value_set_enum(value, val); break; case PROP_LOOP_START: sample = ipatch_sli_zone_get_sample(zone); /* ++ ref sample */ if(sample) { g_object_get(sample, "loop-start", &uval, NULL); g_object_unref(sample); /* -- unref sample */ val = uval; } ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(zone), IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START, &amount); val += (int)amount.sword << 15; ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(zone), IPATCH_SF2_GEN_SAMPLE_LOOP_START, &amount); val += amount.sword; g_value_set_uint(value, CLAMP(val, 0, G_MAXINT)); break; case PROP_LOOP_END: sample = ipatch_sli_zone_get_sample(zone); /* ++ ref sample */ if(sample) { g_object_get(sample, "loop-end", &uval, NULL); g_object_unref(sample); /* -- unref sample */ val = uval; } ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(zone), IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END, &amount); val += (int)amount.sword << 15; ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(zone), IPATCH_SF2_GEN_SAMPLE_LOOP_END, &amount); val += amount.sword; g_value_set_uint(value, CLAMP(val, 0, G_MAXINT)); break; case PROP_ROOT_NOTE: ipatch_sli_zone_get_root_note(zone, value); break; case PROP_FINE_TUNE: ipatch_sli_zone_get_fine_tune(zone, value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } } } static void ipatch_sli_zone_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchSLIZone *src_zone, *dest_zone; IpatchSLISample *refsample; src_zone = IPATCH_SLI_ZONE(src); dest_zone = IPATCH_SLI_ZONE(dest); IPATCH_ITEM_RLOCK(src_zone); /* pass the link to the link handler (if any) */ refsample = (IpatchSLISample *) IPATCH_ITEM_COPY_LINK_FUNC(dest, IPATCH_ITEM(src_zone->sample), link_func, user_data); if(refsample) { ipatch_sli_zone_set_sample(dest_zone, refsample); } /* duplicate generator array */ memcpy(&dest_zone->genarray, &src_zone->genarray, sizeof(IpatchSF2GenArray)); IPATCH_ITEM_RUNLOCK(src_zone); } static void ipatch_sli_zone_item_remove_full(IpatchItem *item, gboolean full) { if(full) { ipatch_sli_zone_set_sample(IPATCH_SLI_ZONE(item), NULL); } if(IPATCH_ITEM_CLASS(ipatch_sli_zone_parent_class)->remove_full) { IPATCH_ITEM_CLASS(ipatch_sli_zone_parent_class)->remove_full(item, full); } } /** * ipatch_sli_zone_new: * * Create a new Spectralis instrument zone object. * * Returns: New Spectralis instrument zone with a reference count of 1. Caller * owns the reference and removing it will destroy the item, unless another * reference is added (if its parented for example). */ IpatchSLIZone * ipatch_sli_zone_new(void) { return (IPATCH_SLI_ZONE(g_object_new(IPATCH_TYPE_SLI_ZONE, NULL))); } /** * ipatch_sli_zone_first: (skip) * @iter: Patch item iterator containing #IpatchSLIZone items * * Gets the first item in a zone iterator. A convenience * wrapper for ipatch_iter_first(). * * Returns: The first zone in @iter or %NULL if empty. */ IpatchSLIZone * ipatch_sli_zone_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_SLI_ZONE(obj)); } else { return (NULL); } } /** * ipatch_sli_zone_next: (skip) * @iter: Patch item iterator containing #IpatchSLIZone items * * Gets the next item in a zone iterator. A convenience wrapper * for ipatch_iter_next(). * * Returns: The next zone in @iter or %NULL if at the end of * the list. */ IpatchSLIZone * ipatch_sli_zone_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_SLI_ZONE(obj)); } else { return (NULL); } } /** * ipatch_sli_zone_set_sample: * @zone: Zone to set sample of * @sample: Sample to set zone to * * Sets the referenced sample of a zone. */ void ipatch_sli_zone_set_sample(IpatchSLIZone *zone, IpatchSLISample *sample) { g_return_if_fail(IPATCH_IS_SLI_ZONE(zone)); g_return_if_fail(IPATCH_IS_SLI_SAMPLE(sample)); ipatch_sli_zone_real_set_sample(zone, sample, TRUE); } static void ipatch_sli_zone_real_set_sample(IpatchSLIZone *zone, IpatchSLISample *sample, gboolean sample_notify) { GValue oldval = { 0 }, newval = { 0 }; IpatchSLISample *oldsample; if(sample_notify) ipatch_item_get_property_fast((IpatchItem *)zone, link_item_pspec, &oldval); if(sample) { g_object_ref(sample); /* ref for zone */ } IPATCH_ITEM_WLOCK(zone); oldsample = zone->sample; zone->sample = sample; IPATCH_ITEM_WUNLOCK(zone); if(oldsample) { g_object_unref(oldsample); } if(sample_notify) { g_value_init(&newval, IPATCH_TYPE_SLI_SAMPLE); g_value_set_object(&newval, sample); ipatch_item_prop_notify((IpatchItem *)zone, link_item_pspec, &newval, &oldval); g_value_unset(&oldval); g_value_unset(&newval); } /* notify title property change */ g_value_init(&newval, G_TYPE_STRING); ipatch_sli_zone_get_title(zone, &newval); ipatch_item_prop_notify((IpatchItem *)zone, ipatch_item_pspec_title, &newval, NULL); g_value_unset(&newval); } /** * ipatch_sli_zone_get_sample: * @zone: Zone to get referenced sample from * * Gets the referenced sample from a zone. * The returned item's reference count is incremented and the caller * is responsible for unrefing it with g_object_unref() when * done with it. * * Returns: (transfer full): Zone's referenced sample or %NULL if global zone. Remember to * unreference the item with g_object_unref() when done with it. */ IpatchSLISample * ipatch_sli_zone_get_sample(IpatchSLIZone *zone) { IpatchSLISample *sample; g_return_val_if_fail(IPATCH_IS_SLI_ZONE(zone), NULL); IPATCH_ITEM_RLOCK(zone); sample = zone->sample; if(sample) { g_object_ref(sample); } IPATCH_ITEM_RUNLOCK(zone); return sample; } /** * ipatch_sli_zone_peek_sample: (skip) * @zone: Zone to get referenced item of * * Like ipatch_sli_zone_get_sample() but does not add a reference to * the returned sample. This function should only be used if a reference * of the returned sample is ensured or only the pointer value is of * interest. * * Returns: Zone's linked sample. * Remember that the item has NOT been referenced. */ IpatchSLISample * ipatch_sli_zone_peek_sample(IpatchSLIZone *zone) { g_return_val_if_fail(IPATCH_IS_SLI_ZONE(zone), NULL); return (zone->sample); /* atomic read */ } libinstpatch-1.1.6/libinstpatch/IpatchSLIZone.h000066400000000000000000000071351400263525300214620ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * Author of this file: (C) 2012 BALATON Zoltan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SLI_ZONE_H__ #define __IPATCH_SLI_ZONE_H__ #include #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchSLIZone IpatchSLIZone; typedef struct _IpatchSLIZoneClass IpatchSLIZoneClass; #define IPATCH_TYPE_SLI_ZONE (ipatch_sli_zone_get_type ()) #define IPATCH_SLI_ZONE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SLI_ZONE, \ IpatchSLIZone)) #define IPATCH_SLI_ZONE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SLI_ZONE, \ IpatchSLIZoneClass)) #define IPATCH_IS_SLI_ZONE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SLI_ZONE)) #define IPATCH_IS_SLI_ZONE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SLI_ZONE)) #define IPATCH_SLI_ZONE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SLI_ZONE, \ IpatchSLIZoneClass)) /* SoundFont zone item */ struct _IpatchSLIZone { IpatchItem parent_instance; /*< private >*/ IpatchSLISample *sample; /* referenced sample */ IpatchSF2GenArray genarray; /* generator array */ int flags; /* misc flags */ }; struct _IpatchSLIZoneClass { IpatchItemClass parent_class; }; /** * IPATCH_SLI_ZONE_UNUSED_FLAG_SHIFT: (skip) */ /* reserve 2 flags */ #define IPATCH_SLI_ZONE_UNUSED_FLAG_SHIFT (IPATCH_ITEM_UNUSED_FLAG_SHIFT + 2) /* a macro for getting or setting a generator value directly, normally ipatch_sli_zone_(get/set)_gen should be used instead, zone is not locked and no flags are set (should only be used on zones with exclusive access) */ #define IPATCH_SLI_ZONE_GEN_AMT(zone, genid) \ ((IpatchSLIZone *)zone)->genarray.values[genid] /* macros for manipulating zone generator set flags directly, should not normally be used except in the case of exclusive access, not locked, etc */ #define IPATCH_SLI_ZONE_GEN_TEST_FLAG(zone, genid) \ IPATCH_SLI_GEN_ARRAY_TEST_FLAG (&((IpatchSLIZone *)(zone))->genarray, genid) #define IPATCH_SLI_ZONE_GEN_SET_FLAG(zone, genid) \ IPATCH_SLI_GEN_ARRAY_SET_FLAG (&((IpatchSLIZone *)(zone))->genarray, genid) #define IPATCH_SLI_ZONE_GEN_CLEAR_FLAG(zone, genid) \ IPATCH_SLI_GEN_ARRAY_CLEAR_FLAG (&((IpatchSLIZone *)(zone))->genarray, genid) GType ipatch_sli_zone_get_type(void); IpatchSLIZone *ipatch_sli_zone_new(void); IpatchSLIZone *ipatch_sli_zone_first(IpatchIter *iter); IpatchSLIZone *ipatch_sli_zone_next(IpatchIter *iter); void ipatch_sli_zone_set_sample(IpatchSLIZone *zone, IpatchSLISample *sample); IpatchSLISample *ipatch_sli_zone_get_sample(IpatchSLIZone *zone); IpatchSLISample *ipatch_sli_zone_peek_sample(IpatchSLIZone *zone); #endif libinstpatch-1.1.6/libinstpatch/IpatchSample.c000066400000000000000000001473421400263525300214200ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSample * @short_description: Sample audio interface * @see_also: * @stability: Stable * * This interface provides a basic API for accessing audio of sample objects. */ #include #include #include #include #include /* for g_unlink */ #include "IpatchSample.h" #include "IpatchSndFile.h" #include "IpatchSampleStoreSndFile.h" #include "builtin_enums.h" #include "sample.h" #include "i18n.h" #include "ipatch_priv.h" /* some public loop type arrays for use with IpatchSample interfaces */ int ipatch_sample_loop_types_standard[] = { IPATCH_SAMPLE_LOOP_NONE, IPATCH_SAMPLE_LOOP_STANDARD, IPATCH_SAMPLE_LOOP_TYPE_TERM /* terminator */ }; int ipatch_sample_loop_types_standard_release[] = { IPATCH_SAMPLE_LOOP_NONE, IPATCH_SAMPLE_LOOP_STANDARD, IPATCH_SAMPLE_LOOP_RELEASE, IPATCH_SAMPLE_LOOP_TYPE_TERM /* terminator */ }; static void ipatch_sample_interface_init(IpatchSampleIface *sample_iface); GType ipatch_sample_get_type(void) { static GType itype = 0; if(!itype) { static const GTypeInfo info = { sizeof(IpatchSampleIface), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) ipatch_sample_interface_init, (GClassFinalizeFunc) NULL }; itype = g_type_register_static(G_TYPE_INTERFACE, "IpatchSample", &info, 0); g_type_interface_add_prerequisite(itype, IPATCH_TYPE_ITEM); } return (itype); } static void ipatch_sample_interface_init(IpatchSampleIface *sample_iface) { g_object_interface_install_property(sample_iface, ipatch_sample_new_property_param_spec("sample-data", G_PARAM_READABLE)); g_object_interface_install_property(sample_iface, ipatch_sample_new_property_param_spec("sample-size", G_PARAM_READABLE)); g_object_interface_install_property(sample_iface, ipatch_sample_new_property_param_spec("sample-format", G_PARAM_READABLE)); g_object_interface_install_property(sample_iface, ipatch_sample_new_property_param_spec("sample-rate", G_PARAM_READABLE)); g_object_interface_install_property(sample_iface, ipatch_sample_new_property_param_spec("loop-type", G_PARAM_READABLE)); g_object_interface_install_property(sample_iface, ipatch_sample_new_property_param_spec("loop-start", G_PARAM_READABLE)); g_object_interface_install_property(sample_iface, ipatch_sample_new_property_param_spec("loop-end", G_PARAM_READABLE)); g_object_interface_install_property(sample_iface, ipatch_sample_new_property_param_spec("root-note", G_PARAM_READABLE)); g_object_interface_install_property(sample_iface, ipatch_sample_new_property_param_spec("fine-tune", G_PARAM_READABLE)); } /** * ipatch_sample_get_loop_types: (skip) * @sample: Object with #IpatchSample interface * * Get an array of supported loop type enums for a sample object. * * Returns: -1 terminated array of #IpatchSampleLoopType values. If no loop * types are supported, then %NULL is returned. Array is internal and should * not be modified or freed. */ int * ipatch_sample_get_loop_types(IpatchSample *sample) { GType type; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), NULL); type = G_OBJECT_TYPE(sample); return (ipatch_sample_type_get_loop_types(type)); } /** * ipatch_sample_type_get_loop_types: (skip) * @type: A GType that has a #IpatchItem interface * * Like ipatch_sample_get_loop_types() but retrieves the supported loop types * from an object type rather than an instance of an object. * * Returns: -1 terminated array of #IpatchSampleLoopType values. If no loop * types are supported, then %NULL is returned. Array is internal and should * not be modified or freed. */ int * ipatch_sample_type_get_loop_types(GType type) { GObjectClass *obj_class; IpatchSampleIface *iface; g_return_val_if_fail(g_type_is_a(type, IPATCH_TYPE_SAMPLE), NULL); obj_class = g_type_class_ref(type); iface = g_type_interface_peek(obj_class, IPATCH_TYPE_SAMPLE); g_type_class_unref(obj_class); return (iface->loop_types); } /** * ipatch_sample_get_loop_types_len: (rename-to ipatch_sample_get_loop_types) * @sample: Object with #IpatchSample interface * @len: (out) (optional): Location to store number of indeces in returned array * (not including -1 terminator), can be %NULL to ignore * * Get an array of supported loop type enums for a sample object. * * Returns: (array length=len): -1 terminated array of #IpatchSampleLoopType values. * If no loop types are supported, then %NULL is returned. Array is internal and should * not be modified or freed. * * Since: 1.1.0 */ int * ipatch_sample_get_loop_types_len(IpatchSample *sample, int *len) { GType type; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), NULL); type = G_OBJECT_TYPE(sample); return (ipatch_sample_type_get_loop_types_len(type, len)); } /** * ipatch_sample_type_get_loop_types_len: * @type: A GType that has a #IpatchItem interface * @len: (out) (optional): Location to store number of indeces in returned array * (not including -1 terminator), can be %NULL to ignore * * Like ipatch_sample_get_loop_types_len() but retrieves the supported loop types * from an object type rather than an instance of an object. * * Returns: (array length=len): -1 terminated array of #IpatchSampleLoopType values. * If no loop types are supported, then %NULL is returned. Array is internal and should * not be modified or freed. * * Since: 1.1.0 */ int * ipatch_sample_type_get_loop_types_len(GType type, int *len) { GObjectClass *obj_class; IpatchSampleIface *iface; int *tp; g_return_val_if_fail(g_type_is_a(type, IPATCH_TYPE_SAMPLE), NULL); obj_class = g_type_class_ref(type); iface = g_type_interface_peek(obj_class, IPATCH_TYPE_SAMPLE); g_type_class_unref(obj_class); if(!iface->loop_types) { return (NULL); } if(len) for(*len = 0, tp = iface->loop_types; *tp != -1; *len = *len + 1); return (iface->loop_types); } /** * ipatch_sample_set_format: * @sample: Sample to set format of * @format: Sample format to assign to sample (see #IpatchSampleWidth, etc) * * Set sample format of a new sample. Should only be assigned once. Same as * assigning to a sample's "sample-format" property. */ void ipatch_sample_set_format(IpatchSample *sample, int format) { g_return_if_fail(IPATCH_IS_SAMPLE(sample)); g_object_set(sample, "sample-format", format, NULL); } /** * ipatch_sample_get_format: * @sample: Sample to get format of * * Get the sample format of a sample. Same as getting a sample's "sample-format" * property. * * Returns: Sample format integer (see #IpatchSampleWidth, etc). */ int ipatch_sample_get_format(IpatchSample *sample) { int format; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), 0); g_object_get(sample, "sample-format", &format, NULL); return (format); } /** * ipatch_sample_set_size: * @sample: Sample to set size of * @size: Size to assign (in frames) * * Set the size of a sample. Should be done once, and only once when created. */ void ipatch_sample_set_size(IpatchSample *sample, guint size) { g_return_if_fail(IPATCH_IS_SAMPLE(sample)); g_object_set(sample, "sample-size", size, NULL); } /** * ipatch_sample_get_size: * @sample: Sample to get size of * @bytes: (out) (optional): Location to store sample size in * bytes (size * frame size) or %NULL to ignore * * Get the size of a sample. Same as getting a sample's "sample-size" * property. * * Returns: Sample size (in frames) */ guint ipatch_sample_get_size(IpatchSample *sample, guint *bytes) { guint size; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), 0); g_object_get(sample, "sample-size", &size, NULL); if(bytes) { *bytes = size * ipatch_sample_get_frame_size(sample); } return (size); } /** * ipatch_sample_get_frame_size: * @sample: Sample to get data frame size of * * A convenience function to get size of a single sample frame for a given * @sample. This is useful for determining buffer allocation sizes when * reading or writing data. * * Returns: Size in bytes of a single sample frame */ int ipatch_sample_get_frame_size(IpatchSample *sample) { g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), 0); return (ipatch_sample_format_size(ipatch_sample_get_format(sample))); } /** * ipatch_sample_get_sample_data: * @sample: Sample to get sample data from * * Get sample data object from a sample. Not every sample object supports this * property, in which case %NULL is returned. * * Returns: (transfer full): Sample data object of the sample or %NULL if not set or unsupported * by this sample type. Caller owns a reference to the returned object. */ IpatchSampleData * ipatch_sample_get_sample_data(IpatchSample *sample) { IpatchSampleData *sampledata; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), NULL); g_object_get(sample, "sample-data", &sampledata, NULL); /* ++ ref */ return (sampledata); /* !! caller takes over ref */ } /** * ipatch_sample_set_sample_data: * @sample: Sample to set sample data of * * Set sample data object of a sample. Not every sample object supports writing * to this property, in which case %FALSE will be returned. * * Returns: %TRUE if the sample supports this property and it was assigned, * %FALSE otherwise. */ gboolean ipatch_sample_set_sample_data(IpatchSample *sample, IpatchSampleData *sampledata) { GParamSpec *pspec; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE); g_return_val_if_fail(!sampledata || IPATCH_IS_SAMPLE_DATA(sampledata), FALSE); pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(sample), "sample-data"); if(!(pspec->flags & G_PARAM_WRITABLE)) { return (FALSE); } g_object_set(sample, "sample-data", sampledata, NULL); return (TRUE); } /** * ipatch_sample_read: (skip) * @sample: Sample to read from * @offset: Offset in frames to read from * @frames: Number of frames to read * @buf: Buffer to store sample data in (should be at least @frames * * sizeof (frame), the frame size can be had from * ipatch_sample_get_frame_size()). * @err: Location to store error info or %NULL * * Read sample data from a sample. This is a convenience function which * opens/reads/closes a #IpatchSampleHandle and is therefore not as efficient * when making multiple accesses. Sample data transform * is also not handled (see ipatch_sample_read_transform()). * * Returns: %TRUE on success, %FALSE on error (in which case * @err may be set). */ gboolean ipatch_sample_read(IpatchSample *sample, guint offset, guint frames, gpointer buf, GError **err) { IpatchSampleHandle handle; gpointer retval; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE); if(!ipatch_sample_handle_open(sample, &handle, 'r', 0, 0, err)) { return (FALSE); } retval = ipatch_sample_handle_read(&handle, offset, frames, buf, err); ipatch_sample_handle_close(&handle); return (retval != NULL); } /** * ipatch_sample_read_size: (rename-to ipatch_sample_read) * @sample: Sample to read from * @offset: Offset in frames to read from * @size: Size of data to read in bytes * @err: Location to store error info or %NULL * * Read sample data from a sample. Like ipatch_sample_read() but * is designed to be GObject introspection friendly and returned buffer is allocated. * * Returns: (array length=size) (element-type guint8) (transfer full): Newly * allocated buffer with read data, %NULL on error (in which case * @err may be set). Free the buffer with g_free() when finished with it. * * Since: 1.1.0 */ gpointer ipatch_sample_read_size(IpatchSample *sample, guint offset, guint size, GError **err) { int frame_size; gpointer buf; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), NULL); g_return_val_if_fail(size > 0, NULL); frame_size = ipatch_sample_get_frame_size(sample); g_return_val_if_fail(frame_size > 0, NULL); g_return_val_if_fail(size % frame_size == 0, NULL); buf = g_malloc(size); // ++ alloc buf if(!ipatch_sample_read(sample, offset, size / frame_size, buf, err)) { g_free(buf); // -- free buf on error return (NULL); } return (buf); // !! caller takes over } /** * ipatch_sample_write: (skip) * @sample: Sample to write to * @offset: Offset in frames to write to * @frames: Number of frames to write * @buf: Buffer of sample data to write (should be at least @frames * * sizeof (frame), the frame size can be had from * ipatch_sample_get_frame_size()). * @err: Location to store error info or %NULL * * Write sample data to a sample. This is a convenience function which * opens/writes/closes a #IpatchSampleHandle and is therefore not as efficient * when making multiple accesses. Sample data transform * is also not handled (see ipatch_sample_write_transform()). * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_sample_write(IpatchSample *sample, guint offset, guint frames, gconstpointer buf, GError **err) { IpatchSampleHandle handle; gboolean retval; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE); if(!ipatch_sample_handle_open(sample, &handle, 'w', 0, 0, err)) { return (FALSE); } retval = ipatch_sample_handle_write(&handle, offset, frames, buf, err); ipatch_sample_handle_close(&handle); return (retval); } /** * ipatch_sample_write_size: (rename-to ipatch_sample_write) * @sample: Sample to write to * @offset: Offset in frames to write to * @buf: (array length=size) (element-type guint8) (transfer none): Buffer of * sample data to write * @size: Size of buffer (must be multiple of audio frame size) * @err: Location to store error info or %NULL * * Write sample data to a sample. Like ipatch_sample_write() but is designed * to be GObject Inspection friendly. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). * * Since: 1.1.0 */ gboolean ipatch_sample_write_size(IpatchSample *sample, guint offset, gconstpointer buf, guint size, GError **err) { int frame_size; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE); g_return_val_if_fail(size > 0, FALSE); frame_size = ipatch_sample_get_frame_size(sample); g_return_val_if_fail(frame_size > 0, FALSE); g_return_val_if_fail(size % frame_size == 0, FALSE); return (ipatch_sample_write(sample, offset, size / frame_size, buf, err)); } /** * ipatch_sample_read_transform: (skip) * @sample: Sample to read from * @offset: Offset in frames to read from * @frames: Number of frames to read * @buf: Buffer to store sample data in (should be at least @frames * * ipatch_sample_format_size() of @format). * @format: Format to transform sample data to (if its the same as the native * format of @sample no transformation occurs) * @channel_map: Channel mapping if @format is set (set to 0 otherwise), use * #IPATCH_SAMPLE_UNITY_CHANNEL_MAP for 1 to 1 channel mapping * (see ipatch_sample_get_transform_funcs() for details). * @err: Location to store error info or %NULL * * Like ipatch_sample_read() but allows for sample transformation. * * Returns: %TRUE on success, %FALSE on error (in which case * @err may be set). */ gboolean ipatch_sample_read_transform(IpatchSample *sample, guint offset, guint frames, gpointer buf, int format, guint32 channel_map, GError **err) { IpatchSampleHandle handle; gpointer retval; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE); if(!ipatch_sample_handle_open(sample, &handle, 'r', format, channel_map, err)) { return (FALSE); } retval = ipatch_sample_handle_read(&handle, offset, frames, buf, err); ipatch_sample_handle_close(&handle); return (retval != NULL); } /** * ipatch_sample_read_transform_size: (rename-to ipatch_sample_read_transform) * @sample: Sample to read from * @offset: Offset in frames to read from * @size: Size of sample data to read (in bytes) after conversion * @format: Format to transform sample data to (if its the same as the native * format of @sample no transformation occurs) * @channel_map: Channel mapping if @format is set (set to 0 otherwise), use * #IPATCH_SAMPLE_UNITY_CHANNEL_MAP for 1 to 1 channel mapping * (see ipatch_sample_get_transform_funcs() for details). * @err: Location to store error info or %NULL * * Like ipatch_sample_read_transform() but is GObject Introspection friendly * and audio buffer is allocated. * * Returns: (array length=size) (element-type guint8) (transfer full): Newly * allocated buffer containing sample data or %NULL on error (in which case * @err may be set). * * Since: 1.1.0 */ gpointer ipatch_sample_read_transform_size(IpatchSample *sample, guint offset, guint size, int format, guint32 channel_map, GError **err) { int frame_size; gpointer buf; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), NULL); g_return_val_if_fail(size > 0, NULL); frame_size = ipatch_sample_format_size(format); g_return_val_if_fail(frame_size > 0, NULL); g_return_val_if_fail(size % frame_size == 0, NULL); buf = g_malloc(size); // ++ alloc buf if(!ipatch_sample_read_transform(sample, offset, size / frame_size, buf, format, channel_map, err)) { g_free(buf); // -- free buf on error return (NULL); } return (buf); // !! caller takes over } /** * ipatch_sample_write_transform: (skip) * @sample: Sample to write to * @offset: Offset in frames to write to * @frames: Number of frames to write * @buf: Buffer of sample data to write (should be at least @frames * * ipatch_sample_format_size() of @format). * @format: Format to transform sample data from (if its the same as the native * format of @sample no transformation occurs) * @channel_map: Channel mapping if @format is set (set to 0 otherwise), use * #IPATCH_SAMPLE_UNITY_CHANNEL_MAP for 1 to 1 channel mapping * (see ipatch_sample_get_transform_funcs() for details). * @err: Location to store error info or %NULL * * Like ipatch_sample_write() but allows for sample transformation. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_sample_write_transform(IpatchSample *sample, guint offset, guint frames, gconstpointer buf, int format, guint32 channel_map, GError **err) { IpatchSampleHandle handle; gboolean retval; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE); if(!ipatch_sample_handle_open(sample, &handle, 'w', format, channel_map, err)) { return (FALSE); } retval = ipatch_sample_handle_write(&handle, offset, frames, buf, err); ipatch_sample_handle_close(&handle); return (retval); } /** * ipatch_sample_write_transform_size: (rename-to ipatch_sample_write_transform) * @sample: Sample to write to * @offset: Offset in frames to write to * @buf: Buffer of sample data to write * @size: Size of data in @buf (must be a multiple of @format frame size) * @format: Format to transform sample data from (if its the same as the native * format of @sample no transformation occurs) * @channel_map: Channel mapping if @format is set (set to 0 otherwise), use * #IPATCH_SAMPLE_UNITY_CHANNEL_MAP for 1 to 1 channel mapping * (see ipatch_sample_get_transform_funcs() for details). * @err: Location to store error info or %NULL * * Like ipatch_sample_write() but allows for sample transformation. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). * * Since: 1.1.0 */ gboolean ipatch_sample_write_transform_size(IpatchSample *sample, guint offset, gconstpointer buf, guint size, int format, guint32 channel_map, GError **err) { int frame_size; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE); g_return_val_if_fail(size > 0, FALSE); frame_size = ipatch_sample_format_size(format); g_return_val_if_fail(frame_size != 0, FALSE); g_return_val_if_fail(size % frame_size == 0, FALSE); return (ipatch_sample_write_transform(sample, offset, size / frame_size, buf, format, channel_map, err)); } /** * ipatch_sample_copy: * @dest_sample: Destination sample to copy data to * @src_sample: Source sample to copy data from * @channel_map: Channel mapping, use #IPATCH_SAMPLE_UNITY_CHANNEL_MAP for 1 to 1 * channel mapping (see ipatch_sample_get_transform_funcs() for details). * @err: Location to store error information or %NULL * * Copy sample data from one sample to another. The two samples may differ * in format, in which case the sample data will be converted. The * @dest_sample must either be the same size in frames as @src_sample or not * yet assigned a size. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_sample_copy(IpatchSample *dest_sample, IpatchSample *src_sample, guint32 channel_map, GError **err) { IpatchSampleHandle dest_handle, src_handle; IpatchSampleTransform *transform; int dest_size, src_size, thissize; gpointer buf; int src_format; int sizeleft, ofs; gboolean retval = FALSE; g_return_val_if_fail(IPATCH_IS_SAMPLE(dest_sample), FALSE); g_return_val_if_fail(IPATCH_IS_SAMPLE(src_sample), FALSE); g_return_val_if_fail(!err || !*err, FALSE); dest_size = ipatch_sample_get_size(dest_sample, NULL); src_size = ipatch_sample_get_size(src_sample, NULL); g_return_val_if_fail(src_size != 0, FALSE); /* If destination size not yet set, assign it */ if(dest_size == 0) { dest_size = src_size; ipatch_sample_set_size(dest_sample, dest_size); } g_return_val_if_fail(dest_size == src_size, FALSE); src_format = ipatch_sample_get_format(src_sample); if(!ipatch_sample_handle_open(dest_sample, &dest_handle, 'w', src_format, channel_map, err)) { return (FALSE); } if(!ipatch_sample_handle_open(src_sample, &src_handle, 'r', 0, 0, err)) { ipatch_sample_handle_close(&dest_handle); return (FALSE); } transform = ipatch_sample_handle_get_transform(&dest_handle); /* ++ ref */ /* Transform should always be set, since we passed a format to ipatch_sample_handle_open */ g_return_val_if_fail(transform != NULL, FALSE); thissize = ipatch_sample_transform_get_max_frames(transform); ipatch_sample_transform_get_buffers(transform, &buf, NULL); sizeleft = src_size; ofs = 0; while(sizeleft > 0) { if(thissize > sizeleft) { thissize = sizeleft; } if(!ipatch_sample_handle_read(&src_handle, ofs, thissize, buf, err)) { goto err; } if(!ipatch_sample_handle_write(&dest_handle, ofs, thissize, buf, err)) { goto err; } ofs += thissize; sizeleft -= thissize; } retval = TRUE; err: ipatch_sample_handle_close(&src_handle); /* -- close source handle */ ipatch_sample_handle_close(&dest_handle); /* -- close destination handle */ return (retval); } /* FIXME-GIR: @sub_format is a dynamic GEnum or -1 */ /** * ipatch_sample_save_to_file: * @sample: Sample to save to file * @filename: File name to save to * @file_format: (type IpatchSndFileFormat): A value from the dynamic GEnum "IpatchSndFileFormat". * @sub_format: A value from the dynamic GEnum "IpatchSndFileSubFormat" or -1 * to calculate optimal value based on the format of @sample. * @err: Location to store error info or %NULL to ignore * * Convenience function to save a sample to a file using libsndfile. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean ipatch_sample_save_to_file(IpatchSample *sample, const char *filename, int file_format, int sub_format, GError **err) { IpatchSample *store; int channels, samplerate, sample_format; int loop_type, loop_start, loop_end, fine_tune, root_note; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE); g_return_val_if_fail(filename != NULL, FALSE); g_return_val_if_fail(!err || !*err, FALSE); g_object_get(sample, "sample-format", &sample_format, "sample-rate", &samplerate, NULL); channels = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(sample_format); sub_format = ipatch_snd_file_sample_format_to_sub_format(sample_format, file_format); if(sub_format == -1) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_PROGRAM, "Invalid libsndfile format parameters"); return (FALSE); } store = ipatch_sample_store_snd_file_new(filename); /* ++ ref new store */ if(!ipatch_sample_store_snd_file_init_write(IPATCH_SAMPLE_STORE_SND_FILE(store), file_format, sub_format, IPATCH_SND_FILE_ENDIAN_FILE, channels, samplerate)) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_PROGRAM, "Invalid libsndfile format parameters"); g_object_unref(store); /* -- unref store */ return (FALSE); } g_object_get(sample, "loop-type", &loop_type, "loop-start", &loop_start, "loop-end", &loop_end, "root-note", &root_note, "fine-tune", &fine_tune, NULL); g_object_set(store, "loop-type", loop_type, "loop-start", loop_start, "loop-end", loop_end, "root-note", root_note, "fine-tune", fine_tune, NULL); if(!ipatch_sample_copy(store, sample, IPATCH_SAMPLE_UNITY_CHANNEL_MAP, err)) { g_object_unref(store); /* -- unref store */ return (FALSE); } g_object_unref(store); /* -- unref store */ return (TRUE); } /** * ipatch_sample_handle_open: * @sample: Sample to open a handle to * @handle: (out): Caller supplied structure to initialize * @mode: Access mode to sample, 'r' for reading and 'w' for writing * @format: Sample format to convert to/from (0 for no conversion or to assign * a transform object with ipatch_sample_handle_set_transform()). * @channel_map: Channel mapping if @format is set (set to 0 otherwise), use * #IPATCH_SAMPLE_UNITY_CHANNEL_MAP for 1 to 1 channel mapping * (see ipatch_sample_get_transform_funcs() for details). * @err: Location to store error information * * Open a handle to a sample for reading or writing sample data. Can optionally * provide data conversion if @format is set. If it is desirable to have more * control over the transform object and buffer allocation, the transform object * can be assigned with ipatch_sample_handle_set_transform(). Note that a sample * transform is acquired if @format is set, even if the format is identical to * the @sample format, as a convenience to always provide a data buffer. * * Returns: %TRUE on success, %FALSE on failure (in which case @err may be set) */ gboolean ipatch_sample_handle_open(IpatchSample *sample, IpatchSampleHandle *handle, char mode, int format, guint32 channel_map, GError **err) { IpatchSampleIface *iface; int sample_format; guint size; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE); g_return_val_if_fail(handle != NULL, FALSE); g_return_val_if_fail(mode == 'r' || mode == 'w', FALSE); g_return_val_if_fail(!format || ipatch_sample_format_verify(format), FALSE); /* Verify sample format was set */ g_object_get(sample, "sample-format", &sample_format, NULL); g_return_val_if_fail(ipatch_sample_format_verify(sample_format), FALSE); /* Verify transform formats and channel mapping, if format is set */ if(format) { if(mode == 'r') { g_return_val_if_fail(ipatch_sample_format_transform_verify(sample_format, format, channel_map), FALSE); } else { g_return_val_if_fail(ipatch_sample_format_transform_verify(format, sample_format, channel_map), FALSE); } } /* Verify sample size is set */ g_object_get(sample, "sample-size", &size, NULL); g_return_val_if_fail(size != 0, FALSE); memset(handle, 0, sizeof(IpatchSampleHandle)); handle->sample = g_object_ref(sample); /* ++ ref sample interface object */ handle->read_mode = mode == 'r'; handle->format = format ? format : sample_format; handle->channel_map = format ? channel_map : IPATCH_SAMPLE_UNITY_CHANNEL_MAP; /* Was format specified? */ if(format != 0) { /* Acquire sample data transform in the proper direction */ if(handle->read_mode) /* ++ grab transform */ handle->transform = ipatch_sample_transform_pool_acquire(sample_format, format, channel_map); else handle->transform = ipatch_sample_transform_pool_acquire(format, sample_format, channel_map); handle->release_transform = TRUE; /* Indicate that transform came from pool */ } iface = IPATCH_SAMPLE_GET_IFACE(sample); handle->read = iface->read; handle->write = iface->write; handle->close = iface->close; /* call interface open method (if any) */ if(iface->open) { if(iface->open(handle, err)) { return (TRUE); } /* Error occurred */ if(handle->transform) /* -- release transform */ { ipatch_sample_transform_pool_release(handle->transform); } g_object_unref(handle->sample); /* -- unref sample */ handle->transform = NULL; handle->sample = NULL; return (FALSE); } else { return (TRUE); /* No open method, assume success */ } } /** * ipatch_sample_handle_close: * @handle: Sample handle to close * * Close a handle previously opened with ipatch_sample_handle_open(). */ void ipatch_sample_handle_close(IpatchSampleHandle *handle) { IpatchSampleIface *iface; g_return_if_fail(handle != NULL); g_return_if_fail(IPATCH_IS_SAMPLE(handle->sample)); iface = IPATCH_SAMPLE_GET_IFACE(handle->sample); /* call interface close method (if any) */ if(iface->close) { iface->close(handle); } if(handle->transform) { /* If transform came from pool, release it, unref otherwise (user assigned) */ if(handle->release_transform) { ipatch_sample_transform_pool_release(handle->transform); /* -- release transform */ } else { ipatch_sample_transform_free(handle->transform); /* -- free transform */ } } g_object_unref(handle->sample); /* -- unref sample */ handle->transform = NULL; handle->sample = NULL; } /** * ipatch_sample_handle_get_transform: * @handle: Sample handle to get transform from * * Get sample transform from a sample handle. Only exists if sample * data conversion is taking place or even if formats are the same but was * implicitly supplied to ipatch_sample_handle_open(). Transform should not be * modified unless it was assigned via ipatch_sample_handle_set_transform(). * * Returns: (transfer none): Sample transform or %NULL if none. */ IpatchSampleTransform * ipatch_sample_handle_get_transform(IpatchSampleHandle *handle) { g_return_val_if_fail(handle != NULL, NULL); g_return_val_if_fail(IPATCH_IS_SAMPLE(handle->sample), NULL); return (handle->transform); } /** * ipatch_sample_handle_set_transform: * @handle: Sample handle to set transform of * @transform: (nullable): Transform to assign, source format must match that of * the handle's sample (read mode) or destination format must match (write mode), * can be %NULL to de-activate sample transformation for @handle. * * Assign a sample transform to a sample handle. Provided for added * control over @transform allocation. A transform can also be automatically * created and assigned with ipatch_sample_handle_open(). Sample transform * allocation is taken over by @handle. */ void ipatch_sample_handle_set_transform(IpatchSampleHandle *handle, IpatchSampleTransform *transform) { g_return_if_fail(handle != NULL); g_return_if_fail(!transform || transform->buf1); if(handle->transform) { /* If transform came from pool, release it, free otherwise (user assigned) */ if(handle->release_transform) { ipatch_sample_transform_pool_release(handle->transform); /* -- release transform */ } else { ipatch_sample_transform_free(handle->transform); /* -- free transform */ } } if(transform) { handle->transform = transform; } else { handle->transform = NULL; } handle->release_transform = FALSE; } /** * ipatch_sample_handle_get_format: * @handle: Sample handle to get format of * * Get the sample format of a sample handle. May differ from the #IpatchSample * format of the handle, if it was opened with a different format and is * therefore being converted. * * Returns: Sample format integer (see #IpatchSampleWidth, etc). */ int ipatch_sample_handle_get_format(IpatchSampleHandle *handle) { g_return_val_if_fail(handle != NULL, 0); g_return_val_if_fail(IPATCH_IS_SAMPLE(handle->sample), 0); if(handle->transform) return (handle->read_mode ? handle->transform->dest_format : handle->transform->src_format); else { return (ipatch_sample_get_format(handle->sample)); } } /** * ipatch_sample_handle_get_frame_size: * @handle: Sample handle to get data frame size of * * A convenience function to get size of a single sample frame for a given * sample @handle. This is useful for determining buffer allocation sizes when * reading or writing data. * * Returns: Size in bytes of a single sample frame */ int ipatch_sample_handle_get_frame_size(IpatchSampleHandle *handle) { return (ipatch_sample_format_size(ipatch_sample_handle_get_format(handle))); } /** * ipatch_sample_handle_get_max_frames: * @handle: Sample handle to get max transform frames of * * A convenience function to get the maximum transform frames that can fit * in the sample transform of @handle. * * Returns: Maximum frames that can be read or written using the sample * transform buffers. 0 if no sample transform is assigned. */ guint ipatch_sample_handle_get_max_frames(IpatchSampleHandle *handle) { g_return_val_if_fail(handle != NULL, 0); g_return_val_if_fail(IPATCH_IS_SAMPLE(handle->sample), 0); if(!handle->transform) { return 0; } return (ipatch_sample_transform_get_max_frames(handle->transform)); } /** * ipatch_sample_handle_read: (skip) * @handle: Sample handle * @offset: Offset in frames to read from * @frames: Number of frames to read * @buf: Buffer to store sample data in (should be at least @frames * * sizeof (frame), the frame size can be had from * ipatch_sample_handle_get_frame_size()). Can be %NULL if transforming * audio data with not more than the maximum frames that can be transformed * at a time, in which case the internal transform buffer pointer will be * returned. * @err: Location to store error info or %NULL * * Read sample data from a sample handle. If the number of * frames read is within the sample transform buffer size and @buf is %NULL * then the transform buffer will be returned (extra copy not needed). * * Returns: Pointer to sample data on success, %NULL on error (in which case * @err may be set). The internal transform buffer will only be returned * if the @buf parameter is %NULL. */ gpointer ipatch_sample_handle_read(IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err) { IpatchSampleTransform *trans; guint readframes, framesize, readbytes; gpointer transbuf, outbuf, bufptr; guint size; g_return_val_if_fail(handle != NULL, NULL); g_return_val_if_fail(IPATCH_IS_SAMPLE(handle->sample), NULL); g_return_val_if_fail(handle->read_mode, NULL); g_return_val_if_fail(!err || !*err, NULL); g_return_val_if_fail(handle->read != NULL, NULL); /* Make sure read does not exceed the sample size */ size = ipatch_sample_get_size(handle->sample, NULL); g_return_val_if_fail(offset + frames <= size, FALSE); trans = handle->transform; if(trans && !handle->manual_transform) /* transforming audio data? */ { readframes = trans->max_frames; transbuf = trans->buf1; /* buffer pointer not supplied? */ if(!buf) { /* extra descriptive for debugging purposes */ g_return_val_if_fail(buf || frames <= readframes, NULL); /* read the sample data */ if(!handle->read(handle, offset, frames, transbuf, err)) { return (NULL); } /* transform the sample data and return - we done! */ return (ipatch_sample_transform_convert_single(trans, frames)); } bufptr = buf; framesize = ipatch_sample_format_size(trans->dest_format); readbytes = readframes * framesize; while(frames > 0) /* must be transformed in blocks */ { if(readframes > frames) { readframes = frames; readbytes = readframes * framesize; } /* read the sample data */ if(!handle->read(handle, offset, readframes, transbuf, err)) { return (NULL); } /* transform the sample data */ outbuf = ipatch_sample_transform_convert_single(trans, readframes); /* copy to caller's buffer */ memcpy(bufptr, outbuf, readbytes); frames -= readframes; offset += readframes; bufptr = (guint8 *)bufptr + readbytes; } } else /* not transforming, do it all in one go */ { g_return_val_if_fail(buf != NULL, NULL); if(!handle->read(handle, offset, frames, buf, err)) { return (NULL); } } return (buf); } /** * ipatch_sample_handle_read_size: (rename-to ipatch_sample_handle_read) * @handle: Sample handle * @offset: Offset in frames to read from * @size: Size of data to read (in bytes), must be a multiple of sample frame size * @err: Location to store error info or %NULL * * Read sample data from a sample handle. Like ipatch_sample_handle_read() but * is GObject Introspection friendly and allocates returned buffer. * * Returns: (array length=size) (element-type guint8) (transfer full): Newly allocated * sample data or %NULL on error (in which case @err may be set) * * Since: 1.1.0 */ gpointer ipatch_sample_handle_read_size(IpatchSampleHandle *handle, guint offset, guint size, GError **err) { gpointer buf; int frame_size; g_return_val_if_fail(handle != NULL, NULL); g_return_val_if_fail(IPATCH_IS_SAMPLE(handle->sample), NULL); g_return_val_if_fail(size > 0, NULL); frame_size = ipatch_sample_handle_get_frame_size(handle); g_return_val_if_fail(frame_size > 0, NULL); g_return_val_if_fail(size % frame_size == 0, NULL); buf = g_malloc(size); // ++ alloc buf if(!ipatch_sample_handle_read(handle, offset, size / frame_size, buf, err)) { g_free(buf); // -- free buf on error return (NULL); } return (buf); // !! caller takes over allocation } /** * ipatch_sample_handle_write: (skip) * @handle: Sample handle * @offset: Offset in frames to write to * @frames: Number of frames to write * @buf: Buffer of sample data to write (should be at least @frames * * sizeof (frame), the frame size can be had from * ipatch_sample_handle_get_frame_size()). Can be %NULL, in which case it is * assumed that the sample data has been loaded into the first buffer of the * handle's sample transform. * @err: Location to store error info or %NULL * * Write sample data to a sample handle. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_sample_handle_write(IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err) { IpatchSampleTransform *trans; guint writeframes, framesize, writebytes; gpointer transbuf, outbuf; gconstpointer bufptr; guint size; g_return_val_if_fail(handle != NULL, FALSE); g_return_val_if_fail(IPATCH_IS_SAMPLE(handle->sample), FALSE); g_return_val_if_fail(!handle->read_mode, FALSE); g_return_val_if_fail(!err || !*err, FALSE); g_return_val_if_fail(handle->write != NULL, FALSE); /* Make sure write does not exceed the sample size */ size = ipatch_sample_get_size(handle->sample, NULL); g_return_val_if_fail(offset + frames <= size, FALSE); trans = handle->transform; if(trans && !handle->manual_transform) /* transforming audio data? */ { writeframes = trans->max_frames; transbuf = trans->buf1; /* buffer pointer not supplied or its the transform buffer? */ if(!buf || buf == transbuf) { /* extra descriptive for debugging purposes */ g_return_val_if_fail(buf || frames <= writeframes, FALSE); outbuf = ipatch_sample_transform_convert_single(trans, frames); /* write the sample data and return - we's done! */ return (handle->write(handle, offset, frames, outbuf, err)); } bufptr = buf; framesize = ipatch_sample_format_size(trans->src_format); writebytes = writeframes * framesize; while(frames > 0) /* must be transformed in blocks */ { if(writeframes > frames) { writeframes = frames; writebytes = writeframes * framesize; } /* copy the block of sample data to transform */ memcpy(transbuf, bufptr, writebytes); /* transform the sample data */ outbuf = ipatch_sample_transform_convert_single(trans, writeframes); /* write the transformed sample data */ if(!handle->write(handle, offset, writeframes, outbuf, err)) { return (FALSE); } frames -= writeframes; offset += writeframes; bufptr = (guint8 *)bufptr + writebytes; } } else /* not transforming, do it all in one go */ { g_return_val_if_fail(buf != NULL, FALSE); if(!handle->write(handle, offset, frames, buf, err)) { return (FALSE); } } return (TRUE); } /** * ipatch_sample_handle_write_size: (rename-to ipatch_sample_handle_write) * @handle: Sample handle * @offset: Offset in frames to write to * @buf: (array length=size) (element-type guint8) (transfer none): Buffer of * sample data to write * @size: Size of @buf in bytes (must be a multiple of sample frame size) * @err: Location to store error info or %NULL * * Write sample data to a sample handle. Like ipatch_sample_handle_write() but * is GObject Introspection friendly. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). * * Since: 1.1.0 */ gboolean ipatch_sample_handle_write_size(IpatchSampleHandle *handle, guint offset, gconstpointer buf, guint size, GError **err) { int frame_size; g_return_val_if_fail(handle != NULL, FALSE); g_return_val_if_fail(IPATCH_IS_SAMPLE(handle->sample), FALSE); frame_size = ipatch_sample_handle_get_frame_size(handle); g_return_val_if_fail(frame_size != 0, FALSE); g_return_val_if_fail(size % frame_size == 0, FALSE); return (ipatch_sample_handle_write(handle, offset, size / frame_size, buf, err)); } /** * ipatch_sample_handle_cascade_open: (skip) * @handle: Already open handle * @sample: The cascade sample containing the actual data * @err: Location to store error information * * This can be called from #IpatchSampleIface.open methods * for objects which contain a pointer to an #IpatchSample that contains the * sample's data. * * Returns: %TRUE on success, %FALSE on failure (in which case @err may be set) */ gboolean ipatch_sample_handle_cascade_open(IpatchSampleHandle *handle, IpatchSample *sample, GError **err) { IpatchSampleIface *iface; g_return_val_if_fail(handle != NULL, FALSE); g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE); iface = IPATCH_SAMPLE_GET_IFACE(sample); g_object_unref(handle->sample); /* -- unref old sample */ handle->sample = g_object_ref(sample); /* ++ ref cascaded sample for new handle */ handle->read = iface->read; handle->write = iface->write; handle->close = iface->close; /* call interface open method (if any) */ if(iface->open) { return (iface->open(handle, err)); } else { return (TRUE); /* No open method, assume success */ } } /** * ipatch_sample_install_property: (skip) * @oclass: Object class to install #IpatchSample property * @property_id: Property ID for set/get property class method * @property_name: #IpatchSample property name to install * * A helper function for objects that have an #IpatchSample interface. * Installs a #IpatchSample interface property for the given object class. * The parameter will be #G_PARAM_READWRITE. * * Returns: The newly created and installed parameter spec. */ GParamSpec * ipatch_sample_install_property(GObjectClass *oclass, guint property_id, const char *property_name) { GParamSpec *pspec; g_return_val_if_fail(G_IS_OBJECT_CLASS(oclass), NULL); g_return_val_if_fail(property_id != 0, NULL); pspec = ipatch_sample_new_property_param_spec(property_name, G_PARAM_READWRITE); g_return_val_if_fail(pspec != NULL, NULL); g_object_class_install_property(oclass, property_id, pspec); return (pspec); } /** * ipatch_sample_install_property_readonly: (skip) * @oclass: Object class to install #IpatchSample property * @property_id: Property ID for set/get property class method * @property_name: #IpatchSample property name to install * * A helper function for objects that have an #IpatchSample interface. * Identical to ipatch_sample_install_property() but installs the property * as readonly and uses g_object_class_override_property() instead of * creating a new #GParamSpec. * * Returns: The newly created and installed parameter spec (GParamSpecOverride). */ GParamSpec * ipatch_sample_install_property_readonly(GObjectClass *oclass, guint property_id, const char *property_name) { g_return_val_if_fail(G_IS_OBJECT_CLASS(oclass), NULL); g_return_val_if_fail(property_id != 0, NULL); g_object_class_override_property(oclass, property_id, property_name); return (g_object_class_find_property(oclass, property_name)); } /** * ipatch_sample_new_property_param_spec: (skip) * @property_name: Name of a #IpatchSample property * @flags: Flags to use for the new #GParamSpec * * Seldom used function that creates a new GParamSpec that is identical to * a #IpatchSample property by the name @property_name, except the flags * can differ. * * Returns: New GParamSpec. */ GParamSpec * ipatch_sample_new_property_param_spec(const char *property_name, GParamFlags flags) { if(strcmp(property_name, "sample-data") == 0) return g_param_spec_object("sample-data", _("Sample data"), _("Sample data"), IPATCH_TYPE_SAMPLE_DATA, flags); else if(strcmp(property_name, "sample-size") == 0) return g_param_spec_uint("sample-size", _("Size"), _("Size in frames"), 0, G_MAXUINT, 0, flags); else if(strcmp(property_name, "sample-format") == 0) return g_param_spec_int("sample-format", _("Sample format"), _("Sample format"), 0, G_MAXINT, IPATCH_SAMPLE_FORMAT_DEFAULT, flags); else if(strcmp(property_name, "sample-rate") == 0) return g_param_spec_int("sample-rate", _("Sample rate"), _("Sampling rate in Hertz"), IPATCH_SAMPLE_RATE_MIN, IPATCH_SAMPLE_RATE_MAX, IPATCH_SAMPLE_RATE_DEFAULT, flags); else if(strcmp(property_name, "loop-type") == 0) return g_param_spec_enum("loop-type", _("Loop type"), _("Loop method type"), IPATCH_TYPE_SAMPLE_LOOP_TYPE, IPATCH_SAMPLE_LOOP_NONE, flags); else if(strcmp(property_name, "loop-start") == 0) return g_param_spec_uint("loop-start", _("Loop start"), _("Start of loop in frames"), 0, G_MAXUINT, 0, flags); else if(strcmp(property_name, "loop-end") == 0) return g_param_spec_uint("loop-end", _("Loop end"), _("Loop end in frames (after loop)"), 0, G_MAXUINT, 0, flags); else if(strcmp(property_name, "root-note") == 0) return g_param_spec_int("root-note", _("Root note"), _("Root MIDI note"), 0, 127, IPATCH_SAMPLE_ROOT_NOTE_DEFAULT, flags); else if(strcmp(property_name, "fine-tune") == 0) return g_param_spec_int("fine-tune", _("Fine tuning"), _("Fine tuning in cents"), -99, 99, 0, flags); else { return (NULL); } } libinstpatch-1.1.6/libinstpatch/IpatchSample.h000066400000000000000000000302401400263525300214110ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SAMPLE_H__ #define __IPATCH_SAMPLE_H__ #include #include #include /* forward type declarations */ typedef struct _IpatchSample IpatchSample; /* dummy typedef */ typedef struct _IpatchSampleIface IpatchSampleIface; typedef struct _IpatchSampleHandle IpatchSampleHandle; #include #define IPATCH_TYPE_SAMPLE (ipatch_sample_get_type ()) #define IPATCH_SAMPLE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SAMPLE, \ IpatchSample)) #define IPATCH_SAMPLE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SAMPLE, \ IpatchSampleIface)) #define IPATCH_IS_SAMPLE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SAMPLE)) #define IPATCH_SAMPLE_GET_IFACE(obj) \ (G_TYPE_INSTANCE_GET_INTERFACE ((obj), IPATCH_TYPE_SAMPLE, \ IpatchSampleIface)) /** * IpatchSampleHandleOpenFunc: * @handle: Caller supplied structure to initialize * @err: Location to store error information * * #IpatchSample interface method function type to open a sample for reading * or writing. This method is optional for an #IpatchSample interface and if * not specified then it is assumed that the open was successful and nothing * additional need be done. All fields of @handle structure are already * initialized, except data1, * data2 and data3 * which are available for the interface implementation. * * Returns: %TRUE on success, %FALSE otherwise (in which case an error should * optionally be stored in @err). */ typedef gboolean(*IpatchSampleHandleOpenFunc)(IpatchSampleHandle *handle, GError **err); /** * IpatchSampleHandleCloseFunc: * @handle: Sample handle to close (as returned from #IpatchSampleHandleOpenFunc) * * #IpatchSample interface method to free any resources allocated in * #IpatchSampleOpenFunc to @handle. This method is optional for an * #IpatchSample interface and need not be specified if nothing needs to be done * to release any resources allocated by #IpatchSampleHandleOpenFunc. */ typedef void (*IpatchSampleHandleCloseFunc)(IpatchSampleHandle *handle); /** * IpatchSampleHandleReadFunc: * @handle: Handle to read from (as returned from #IpatchSampleOpenFunc) * @offset: Offset in sample to start read from (in frames), use * #IPATCH_SAMPLE_CUROFS to use current offset (starts at 0 if not specified) * @frames: Size of sample data to read (in frames) * @buf: Buffer to store sample data in * @err: Location to store error information * * #IpatchSample interface method function type to read data from a * sample handle. Can be %NULL in #IpatchSampleIface if sample data is not * readable. Sample data should be stored in its native format. * * Returns: Should return %TRUE on success, %FALSE otherwise (in which case * an error should optionally be stored in @err). */ typedef gboolean(*IpatchSampleHandleReadFunc)(IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err); /** * IpatchSampleHandleWriteFunc: * @handle: Handle to write to (as returned from #IpatchSampleOpenFunc) * @offset: Offset in sample to start write to (in frames), use * #IPATCH_SAMPLE_CUROFS to use current offset (starts at 0 if not specified) * @frames: Size of sample data to write (in frames) * @buf: Buffer to store sample data in * @err: Location to store error information * * #IpatchSample interface method function type to write data to a * sample handle. Can be %NULL in #IpatchSampleIface if sample data is not * writable. Sample data will be supplied in its native format. * * Returns: Should return %TRUE on success, %FALSE otherwise (in which case * an error should optionally be stored in @err). */ typedef gboolean(*IpatchSampleHandleWriteFunc)(IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err); /* Sample interface */ struct _IpatchSampleIface { GTypeInterface parent_class; IpatchSampleHandleOpenFunc open; IpatchSampleHandleCloseFunc close; IpatchSampleHandleReadFunc read; IpatchSampleHandleWriteFunc write; int *loop_types; /* -1 terminated array of supp. loop types (NULL = none) */ }; /* Sample handle for I/O operations */ struct _IpatchSampleHandle { IpatchSample *sample; /* The sample which this handle applies to */ IpatchSampleTransform *transform; /* Set if sample is being converted */ IpatchSampleHandleReadFunc read; /* Read method pointer (copied from IpatchItem interface) */ IpatchSampleHandleWriteFunc write; /* Write method pointer (copied from IpatchItem interface) */ IpatchSampleHandleCloseFunc close; /* Close method pointer (copied from IpatchItem interface) */ guint32 read_mode : 1; /* TRUE if read mode, FALSE if write mode */ guint32 manual_transform : 1; /* Methods handle sample transform */ guint32 release_transform : 1; /* TRUE if transform should be released from transform pool */ guint32 format : 12; /* Format to transform to */ guint32 reserved : 17; guint32 channel_map; /* Channel map for multi-channel audio transform */ gpointer data1; /* sample interface defined */ gpointer data2; /* sample interface defined */ gpointer data3; /* sample interface defined */ gpointer data4; /* sample interface defined */ guint32 reserved2; }; /** * IpatchSampleLoopType: * @IPATCH_SAMPLE_LOOP_NONE: No loop. * @IPATCH_SAMPLE_LOOP_STANDARD: Standard loop. * @IPATCH_SAMPLE_LOOP_RELEASE: Loop till note release stage. * @IPATCH_SAMPLE_LOOP_PINGPONG: Play forward and then in reverse continously. * * Sample looping type. */ typedef enum { IPATCH_SAMPLE_LOOP_NONE, IPATCH_SAMPLE_LOOP_STANDARD, IPATCH_SAMPLE_LOOP_RELEASE, IPATCH_SAMPLE_LOOP_PINGPONG } IpatchSampleLoopType; /** * IPATCH_SAMPLE_FORMAT_DEFAULT: * * Default sample format for #IpatchSample interface. */ #define IPATCH_SAMPLE_FORMAT_DEFAULT (IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_MONO \ | IPATCH_SAMPLE_LENDIAN | IPATCH_SAMPLE_SIGNED) /** * IPATCH_SAMPLE_RATE_MIN: (skip) * * Minimum sample rate. * SoundFont spec says 8000 Hz is minimum guaranteed, seen lots of smaller values though. */ #define IPATCH_SAMPLE_RATE_MIN 100 /** * IPATCH_SAMPLE_RATE_MAX: (skip) * * Maximum sample rate. */ #define IPATCH_SAMPLE_RATE_MAX 192000 /** * IPATCH_SAMPLE_RATE_DEFAULT: (skip) * * Default sample rate. */ #define IPATCH_SAMPLE_RATE_DEFAULT 44100 /** * IPATCH_SAMPLE_ROOT_NOTE_DEFAULT: (skip) * * Default root note. */ #define IPATCH_SAMPLE_ROOT_NOTE_DEFAULT 60 /** * IPATCH_SAMPLE_LOOP_TYPE_TERM: * * Value used for terminating list of supported loop types. */ #define IPATCH_SAMPLE_LOOP_TYPE_TERM (-1) /** * IPATCH_SAMPLE_HANDLE_FORMAT: * @handle: Sample handle * * Macro to access transform sample format of a sample handle. * * Returns: Sample transform format. */ #define IPATCH_SAMPLE_HANDLE_FORMAT(handle) ((handle)->format) /* some convenient loop_type arrays for use by IpatchSample interfaces */ extern int ipatch_sample_loop_types_standard[]; extern int ipatch_sample_loop_types_standard_release[]; GType ipatch_sample_get_type(void); int *ipatch_sample_get_loop_types(IpatchSample *sample); int *ipatch_sample_type_get_loop_types(GType type); int *ipatch_sample_get_loop_types_len(IpatchSample *sample, int *len); int *ipatch_sample_type_get_loop_types_len(GType type, int *len); void ipatch_sample_set_format(IpatchSample *sample, int format); int ipatch_sample_get_format(IpatchSample *sample); void ipatch_sample_set_size(IpatchSample *sample, guint size); guint ipatch_sample_get_size(IpatchSample *sample, guint *bytes); int ipatch_sample_get_frame_size(IpatchSample *sample); IpatchSampleData *ipatch_sample_get_sample_data(IpatchSample *sample); gboolean ipatch_sample_set_sample_data(IpatchSample *sample, IpatchSampleData *sampledata); gboolean ipatch_sample_read(IpatchSample *sample, guint offset, guint frames, gpointer buf, GError **err); gpointer ipatch_sample_read_size(IpatchSample *sample, guint offset, guint size, GError **err); gboolean ipatch_sample_write(IpatchSample *sample, guint offset, guint frames, gconstpointer buf, GError **err); gboolean ipatch_sample_write_size(IpatchSample *sample, guint offset, gconstpointer buf, guint size, GError **err); gboolean ipatch_sample_read_transform(IpatchSample *sample, guint offset, guint frames, gpointer buf, int format, guint32 channel_map, GError **err); gpointer ipatch_sample_read_transform_size(IpatchSample *sample, guint offset, guint size, int format, guint32 channel_map, GError **err); gboolean ipatch_sample_write_transform(IpatchSample *sample, guint offset, guint frames, gconstpointer buf, int format, guint32 channel_map, GError **err); gboolean ipatch_sample_write_transform_size(IpatchSample *sample, guint offset, gconstpointer buf, guint size, int format, guint32 channel_map, GError **err); gboolean ipatch_sample_copy(IpatchSample *dest_sample, IpatchSample *src_sample, guint32 channel_map, GError **err); gboolean ipatch_sample_save_to_file(IpatchSample *sample, const char *filename, int file_format, int sub_format, GError **err); gboolean ipatch_sample_handle_open(IpatchSample *sample, IpatchSampleHandle *handle, char mode, int format, guint32 channel_map, GError **err); void ipatch_sample_handle_close(IpatchSampleHandle *handle); IpatchSampleTransform *ipatch_sample_handle_get_transform(IpatchSampleHandle *handle); void ipatch_sample_handle_set_transform(IpatchSampleHandle *handle, IpatchSampleTransform *transform); int ipatch_sample_handle_get_format(IpatchSampleHandle *handle); int ipatch_sample_handle_get_frame_size(IpatchSampleHandle *handle); guint ipatch_sample_handle_get_max_frames(IpatchSampleHandle *handle); gpointer ipatch_sample_handle_read(IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err); gpointer ipatch_sample_handle_read_size(IpatchSampleHandle *handle, guint offset, guint size, GError **err); gboolean ipatch_sample_handle_write(IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err); gboolean ipatch_sample_handle_write_size(IpatchSampleHandle *handle, guint offset, gconstpointer buf, guint size, GError **err); gboolean ipatch_sample_handle_cascade_open(IpatchSampleHandle *handle, IpatchSample *sample, GError **err); GParamSpec *ipatch_sample_install_property(GObjectClass *oclass, guint property_id, const char *property_name); GParamSpec *ipatch_sample_install_property_readonly(GObjectClass *oclass, guint property_id, const char *property_name); GParamSpec *ipatch_sample_new_property_param_spec(const char *property_name, GParamFlags flags); #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleData.c000066400000000000000000001547711400263525300222160ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSampleData * @short_description: Sample data proxy object. * @see_also: #IpatchSampleStore * @stability: Stable * * An object which acts as a proxy for sample data and one or more cached * versions of the same audio but potentially differing in sample format * and/or storage media. #IpatchSampleStore is used instead of the * #IpatchSample interface, for increased performance. */ #include #include #include #include #include "IpatchSampleData.h" #include "IpatchSampleStoreCache.h" #include "IpatchSampleStoreRam.h" #include "IpatchSampleStoreSwap.h" #include "IpatchSample.h" #include "ipatch_priv.h" #include "builtin_enums.h" #include "config.h" enum { PROP_0, PROP_TITLE, PROP_SAMPLE_SIZE, PROP_SAMPLE_FORMAT, PROP_SAMPLE_RATE, PROP_SAMPLE_DATA, PROP_LOOP_TYPE, PROP_LOOP_START, PROP_LOOP_END, PROP_ROOT_NOTE, PROP_FINE_TUNE }; /* Info structure used to ensure that duplicate sample caching does not occur */ typedef struct { IpatchSampleStore *store; int format; guint32 channel_map; } CachingInfo; static void _ipatch_sample_data_free_caching_info(CachingInfo *data); static void ipatch_sample_data_sample_iface_init(IpatchSampleIface *iface); static gboolean ipatch_sample_data_sample_iface_open(IpatchSampleHandle *handle, GError **err); static void ipatch_sample_data_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sample_data_dispose(GObject *gobject); static void ipatch_sample_data_release_store(IpatchSampleStore *store); static gint sample_cache_clean_sort(gconstpointer a, gconstpointer b); /* master sample data list and lock */ G_LOCK_DEFINE_STATIC(sample_data_list); static GSList *sample_data_list = NULL; /* Lock for metric variables below */ G_LOCK_DEFINE_STATIC(sample_cache_vars); static guint64 sample_cache_total_size = 0; /* Total size of cached samples */ static guint64 sample_cache_unused_size = 0; /* Size of unused cached samples */ /* Variables used to ensure that duplicate sample caching does not occur */ static GMutex *caching_mutex; static GCond *caching_cond; static GSList *caching_list = NULL; G_DEFINE_TYPE_WITH_CODE(IpatchSampleData, ipatch_sample_data, IPATCH_TYPE_ITEM, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_sample_data_sample_iface_init)) /* ----- Initialization/deinitialization of lists ---------------------------*/ /* Initialize lists */ void _ipatch_sample_data_init(void) { sample_cache_total_size = 0; /* Total size of cached samples */ sample_cache_unused_size = 0; /* Size of unused cached samples */ sample_data_list = NULL; caching_list = NULL; } /* Free lists */ void _ipatch_sample_data_deinit(void) { g_slist_free(sample_data_list); g_slist_free_full(caching_list, (GDestroyNotify)_ipatch_sample_data_free_caching_info); } /* free data list */ static void _ipatch_sample_data_free_caching_info(CachingInfo *data) { g_slice_free(CachingInfo, data); } /* ----- IpatchSampleData object functions ---------------------------------*/ /** * ipatch_get_sample_data_list: * * Creates an object list copy of the master sample data list (all * existing sample data objects). * * Returns: (transfer full): New object list populated with all #IpatchSampleData objects * with a reference count of 1 which the caller owns, removing the reference * will free the list. * * Since: 1.1.0 */ IpatchList * ipatch_get_sample_data_list(void) { IpatchList *list; GSList *p; list = ipatch_list_new(); /* ++ ref new list */ G_LOCK(sample_data_list); for(p = sample_data_list; p; p = p->next) { list->items = g_list_prepend(list->items, p->data); g_object_ref(p->data); /* ++ ref object for list */ } G_UNLOCK(sample_data_list); return (list); /* !! caller takes over reference */ } /* GHFunc for g_hash_table_foreach() to remove a store from its parent sample data */ static void remove_stores(gpointer key, gpointer value, gpointer user_data) { IpatchSampleStore *store = key; IpatchSampleData *sampledata; sampledata = (IpatchSampleData *)ipatch_item_get_parent((IpatchItem *)store); // ++ ref parent sample data ipatch_sample_data_remove(sampledata, store); g_object_unref(sampledata); // -- unref parent sample data } /** * ipatch_migrate_file_sample_data: * @oldfile: (nullable): Old file to migrate samples from (can be %NULL, but not both @oldfile and @newfile) * @newfile: (nullable): New file which has stores which may be used for migration * (can be %NULL, but not both @oldfile and @newfile) * @filename: (nullable): File name used for replace if @oldfile is %NULL and * #IPATCH_SAMPLE_DATA_MIGRATE_REPLACE is set in @flags * @flags: (type IpatchSampleDataMigrateFlags): Flag options for migration * @err: Location to store error info or %NULL to ignore * * Used for migrating sample data when saving, deleting, replacing or closing instrument files. * * If @oldfile is set, sample data will be migrated for those that have native sample references to it, and the * old sample stores are removed. * * When saving a file, @newfile can be set. In this case new #IpatchSampleStore objects should have already been * added to their applicable #IpatchSampleData objects. #IpatchSampleData objects will be migrated to these stores * if they match the native format and the criteria set by the @flags parameter. * * If sample data needs to be migrated but there is no format identical store from @newfile, then * a new duplicate #IpatchSampleStoreSwap will be created and set as the new native sample. * * If the #IPATCH_SAMPLE_DATA_MIGRATE_REMOVE_NEW_IF_UNUSED flag is set in @flags, then unused #IpatchSampleStore * objects referencing @newfile will be removed if unused. * * The #IPATCH_SAMPLE_DATA_MIGRATE_TO_NEWFILE flag can be used to migrate all native compatible samples to @newfile. * Default is to only migrate samples which reference @oldfile or swap. * * The #IPATCH_SAMPLE_DATA_MIGRATE_LEAVE_IN_SWAP flag can be used to not migrate samples out of swap. The default * is to migrate samples out of swap to @newfile if possible. * * The #IPATCH_SAMPLE_DATA_MIGRATE_REPLACE flag can be used to replace @oldfile with @newfile using * ipatch_file_replace(). If @oldfile is not set, @filename can be used to replace a file, which should not * be referenced by any other #IpatchFile object. If neither of these is set, then this flag is ignored. * * NOTE - Not really thread safe. It is assumed that sample stores referencing @oldfile or @newfile * will not be added or removed (respectively) by other threads during this function. The side * effect of this would potentially be added samples still referencing @oldfile or removed samples * being re-added. * * Returns: %TRUE on succcess, %FALSE otherwise (in which case @err may be set) * * Since: 1.1.0 */ gboolean ipatch_migrate_file_sample_data(IpatchFile *oldfile, IpatchFile *newfile, const char *filename, guint flags, GError **err) { IpatchSampleData *sampledata; IpatchSampleStore *old_store, *store, *native_store, *new_store; IpatchList *old_list, *store_list; GHashTable *replace_hash; // Hash of new stores to use to replace native stores GHashTable *remove_hash; // Hash of new stores to remove GHashTable *swap_hash; // Hash of created swap stores to replace native stores GHashTableIter iter; gpointer key, value; GList *p, *p2; int native_fmt, new_fmt; int sample_rate; gboolean is_swap_store; g_return_val_if_fail(!oldfile || IPATCH_IS_FILE(oldfile), FALSE); g_return_val_if_fail(!newfile || IPATCH_IS_FILE(newfile), FALSE); g_return_val_if_fail(oldfile || newfile, FALSE); g_return_val_if_fail(!err || !*err, FALSE); replace_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, g_object_unref, NULL); remove_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, g_object_unref, NULL); swap_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, g_object_unref, NULL); if(oldfile) { old_list = ipatch_file_get_refs_by_type(oldfile, IPATCH_TYPE_SAMPLE_STORE); // ++ ref list of stores referencing oldfile p = old_list->items; } else { p = NULL; } // Loop over sample stores referencing old file for(; p; p = p->next) { old_store = (IpatchSampleStore *)(p->data); sampledata = (IpatchSampleData *)ipatch_item_get_parent((IpatchItem *)old_store); // ++ ref parent sample data if(!sampledata) { continue; // Probably shouldn't happen, right? } store_list = ipatch_sample_data_get_samples(sampledata); // ++ ref sample data store list if(!store_list || !store_list->items) // Shouldn't happen, but.. { if(store_list) { g_object_unref(store_list); // -- unref store list } g_object_unref(sampledata); // -- unref sample data continue; } native_store = (IpatchSampleStore *)(store_list->items->data); // Native sample store new_store = NULL; if(newfile) // New file provided? { native_fmt = ipatch_sample_store_get_format(native_store); // Search for sample store referencing new file for(p2 = store_list->items->next; p2; p2 = p2->next) { store = (IpatchSampleStore *)(p2->data); if(ipatch_file_test_ref_object(newfile, (GObject *)store)) { new_fmt = ipatch_sample_store_get_format(store); break; } } if(p2) { new_store = store; } } // Should sample be migrated? if(native_store == old_store // If native store is in old file, must be migrated || ((new_store && new_fmt == native_fmt) // If there is a new store of compatible format.. && ((flags & IPATCH_SAMPLE_DATA_MIGRATE_TO_NEWFILE) // .. and to newfile flag specified || (!(flags & IPATCH_SAMPLE_DATA_MIGRATE_LEAVE_IN_SWAP) && IPATCH_IS_SAMPLE_STORE_SWAP(native_store))))) // .. or native store is in swap and not leave in swap { // If no store in newfile or incompatible format - migrate to swap if(!new_store || new_fmt != native_fmt) { // Add new store to remove list if requested if(new_store && (flags & IPATCH_SAMPLE_DATA_MIGRATE_REMOVE_NEW_IF_UNUSED)) { g_hash_table_insert(remove_hash, g_object_ref(new_store), GUINT_TO_POINTER(1)); // ++ ref for remove hash } g_object_get(old_store, "sample-rate", &sample_rate, NULL); store = (IpatchSampleStore *)ipatch_sample_store_swap_new(); // ++ ref new swap sample store g_object_set(store, "sample-rate", sample_rate, NULL); ipatch_sample_data_add(sampledata, store); if(!ipatch_sample_copy((IpatchSample *)store, (IpatchSample *)old_store, IPATCH_SAMPLE_UNITY_CHANNEL_MAP, err)) { g_object_unref(store); // -- unref swap sample store g_object_unref(sampledata); // -- unref sample data g_hash_table_destroy(replace_hash); // -- Free replace hash g_hash_table_destroy(remove_hash); // -- Free remove hash g_hash_table_foreach(swap_hash, remove_stores, NULL); // -- Remove swap stores g_hash_table_destroy(swap_hash); // -- Free swap hash g_object_unref(store_list); // -- unref sample data store list if(oldfile) { g_object_unref(old_list); // -- unref store list } return (FALSE); } g_hash_table_insert(swap_hash, store, GUINT_TO_POINTER(1)); // !! hash takes over swap reference } // Compatible store in newfile - add to replace list else { g_hash_table_insert(replace_hash, g_object_ref(new_store), GUINT_TO_POINTER(1)); // ++ ref for replace hash } } // Migration not necessary for this sample - remove new store if present and REMOVE_NEW_IF_UNUSED requested else if(new_store && (flags & IPATCH_SAMPLE_DATA_MIGRATE_REMOVE_NEW_IF_UNUSED)) { g_hash_table_insert(remove_hash, g_object_ref(new_store), GUINT_TO_POINTER(1)); // ++ ref new store for remove hash } g_object_unref(store_list); // -- unref sample data store list g_object_unref(sampledata); // -- unref sample data } if(oldfile) { g_object_unref(old_list); // -- unref store list } // Replace oldfile/filename with newfile if requested if((flags & IPATCH_SAMPLE_DATA_MIGRATE_REPLACE) && newfile) { if((oldfile && !ipatch_file_replace(newfile, oldfile, err)) || (!oldfile && filename && !ipatch_file_rename(newfile, filename, err))) { g_hash_table_destroy(replace_hash); // -- Free replace hash g_hash_table_destroy(remove_hash); // -- Free remove hash g_hash_table_foreach(swap_hash, remove_stores, NULL); // -- Remove swap stores g_hash_table_destroy(swap_hash); // -- Free swap hash return (FALSE); } } // Process new stores if newfile specified if(newfile) { store_list = ipatch_file_get_refs_by_type(newfile, IPATCH_TYPE_SAMPLE_STORE); // ++ ref list of stores referencing newfile for(p = store_list->items; p; p = p->next) { store = (IpatchSampleStore *)(p->data); if(g_hash_table_lookup(replace_hash, store)) { continue; // Already in replace hash? - skip } // If TO_NEWFILE or not LEAVE_IN_SWAP and is in swap, check if sample can be migrated to this store if((flags & IPATCH_SAMPLE_DATA_MIGRATE_TO_NEWFILE) || !(flags & IPATCH_SAMPLE_DATA_MIGRATE_LEAVE_IN_SWAP)) { sampledata = (IpatchSampleData *)ipatch_item_get_parent((IpatchItem *)store); // ++ ref parent sample data if(!sampledata) { continue; // Probably shouldn't happen, right? } native_store = ipatch_sample_data_get_native_sample(sampledata); // ++ ref native sample store g_object_unref(sampledata); // -- unref sample data if(native_store) { native_fmt = ipatch_sample_store_get_format(native_store); is_swap_store = IPATCH_IS_SAMPLE_STORE_SWAP(native_store); g_object_unref(native_store); // -- unref native store (only need the pointer value from here on) if(store == native_store) { continue; // already the native store? - skip } // Is format compatible and TO_NEWFILE flag set or native store is swap and LEAVE_IN_SWAP flag not set if(native_fmt == ipatch_sample_store_get_format(store) && ((flags & IPATCH_SAMPLE_DATA_MIGRATE_TO_NEWFILE) || (is_swap_store && !(flags & IPATCH_SAMPLE_DATA_MIGRATE_LEAVE_IN_SWAP)))) { g_hash_table_insert(replace_hash, g_object_ref(store), GUINT_TO_POINTER(1)); // ++ ref store for replace hash continue; } } // if native_store } // if TO_NEWFILE or !LEAVE_IN_SWAP if(flags & IPATCH_SAMPLE_DATA_MIGRATE_REMOVE_NEW_IF_UNUSED) { g_hash_table_insert(remove_hash, g_object_ref(store), GUINT_TO_POINTER(1)); // ++ ref store for remove hash } } g_object_unref(store_list); // -- unref newfile stores } // Replace native stores with those in replace hash (already added to sample data objects) for(g_hash_table_iter_init(&iter, replace_hash); g_hash_table_iter_next(&iter, &key, &value);) { store = (IpatchSampleStore *)key; sampledata = (IpatchSampleData *)ipatch_item_get_parent((IpatchItem *)store); // ++ ref parent sample data ipatch_sample_data_replace_native_sample(sampledata, store); g_object_unref(sampledata); // -- unref sample data } // Replace native stores with those in swap hash (already added to sample data objects) for(g_hash_table_iter_init(&iter, swap_hash); g_hash_table_iter_next(&iter, &key, &value);) { store = (IpatchSampleStore *)key; sampledata = (IpatchSampleData *)ipatch_item_get_parent((IpatchItem *)store); // ++ ref parent sample data ipatch_sample_data_replace_native_sample(sampledata, store); g_object_unref(sampledata); // -- unref sample data } // Remove stores in remove hash for(g_hash_table_iter_init(&iter, remove_hash); g_hash_table_iter_next(&iter, &key, &value);) { store = (IpatchSampleStore *)key; sampledata = (IpatchSampleData *)ipatch_item_get_parent((IpatchItem *)store); // ++ ref parent sample data ipatch_sample_data_remove(sampledata, store); g_object_unref(sampledata); // -- unref sample data } g_hash_table_destroy(replace_hash); // -- Free replace hash g_hash_table_destroy(remove_hash); // -- Free remove hash g_hash_table_destroy(swap_hash); // -- Free swap hash return (TRUE); } static void ipatch_sample_data_sample_iface_init(IpatchSampleIface *iface) { iface->open = ipatch_sample_data_sample_iface_open; } static gboolean ipatch_sample_data_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchSampleData *sampledata = IPATCH_SAMPLE_DATA(handle->sample); IpatchSample *sample = NULL; gboolean retval; IPATCH_ITEM_RLOCK(sampledata); if(sampledata->samples) { sample = g_object_ref(sampledata->samples->data); /* ++ ref */ } IPATCH_ITEM_RUNLOCK(sampledata); g_return_val_if_fail(sample != NULL, FALSE); retval = ipatch_sample_handle_cascade_open(handle, sample, err); g_object_unref(sample); /* -- unref sample */ return (retval); } static void ipatch_sample_data_class_init(IpatchSampleDataClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->dispose = ipatch_sample_data_dispose; obj_class->get_property = ipatch_sample_data_get_property; g_object_class_override_property(obj_class, PROP_TITLE, "title"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_SIZE, "sample-size"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_FORMAT, "sample-format"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_RATE, "sample-rate"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_DATA, "sample-data"); ipatch_sample_install_property_readonly(obj_class, PROP_LOOP_TYPE, "loop-type"); ipatch_sample_install_property_readonly(obj_class, PROP_LOOP_START, "loop-start"); ipatch_sample_install_property_readonly(obj_class, PROP_LOOP_END, "loop-end"); ipatch_sample_install_property_readonly(obj_class, PROP_ROOT_NOTE, "root-note"); ipatch_sample_install_property_readonly(obj_class, PROP_FINE_TUNE, "fine-tune"); caching_mutex = g_mutex_new(); caching_cond = g_cond_new(); } static void ipatch_sample_data_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSampleData *sampledata = IPATCH_SAMPLE_DATA(object); IpatchSampleStore *store = NULL; IPATCH_ITEM_RLOCK(sampledata); if(sampledata->samples) { store = g_object_ref(sampledata->samples->data); /* ++ ref */ } IPATCH_ITEM_RUNLOCK(sampledata); g_return_if_fail(store != NULL); switch(property_id) { case PROP_TITLE: g_object_get_property((GObject *)store, "title", value); break; case PROP_SAMPLE_SIZE: g_value_set_uint(value, ipatch_sample_store_get_size(store)); break; case PROP_SAMPLE_FORMAT: g_value_set_int(value, ipatch_sample_store_get_format(store)); break; case PROP_SAMPLE_RATE: g_value_set_int(value, ipatch_sample_store_get_rate(store)); break; case PROP_SAMPLE_DATA: g_value_set_object(value, sampledata); break; case PROP_LOOP_TYPE: g_object_get_property((GObject *)store, "loop-type", value); break; case PROP_LOOP_START: g_object_get_property((GObject *)store, "loop-start", value); break; case PROP_LOOP_END: g_object_get_property((GObject *)store, "loop-end", value); break; case PROP_ROOT_NOTE: g_object_get_property((GObject *)store, "root-note", value); break; case PROP_FINE_TUNE: g_object_get_property((GObject *)store, "fine-tune", value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } g_object_unref(store); /* -- unref store */ } static void ipatch_sample_data_init(IpatchSampleData *sampledata) { g_atomic_int_set(&sampledata->usecount, 0); // Initial use count is 0 } static void ipatch_sample_data_dispose(GObject *gobject) { IpatchSampleData *sampledata = IPATCH_SAMPLE_DATA(gobject); GSList *p; IPATCH_ITEM_WLOCK(sampledata); for(p = sampledata->samples; p; p = g_slist_delete_link(p, p)) { ipatch_sample_data_release_store(IPATCH_SAMPLE_STORE(p->data)); } sampledata->samples = NULL; IPATCH_ITEM_WUNLOCK(sampledata); if(G_OBJECT_CLASS(ipatch_sample_data_parent_class)->dispose) { G_OBJECT_CLASS(ipatch_sample_data_parent_class)->dispose(gobject); } } /** * ipatch_sample_data_new: * * Create a new sample data object. * * Returns: New sample data with a reference count of 1 which the caller owns. * Use count should be incremented when added to an active instrument sample * using ipatch_sample_data_used() which will add it to the pool of sample data objects. */ IpatchSampleData * ipatch_sample_data_new(void) { return (IPATCH_SAMPLE_DATA(g_object_new(IPATCH_TYPE_SAMPLE_DATA, NULL))); } /** * ipatch_sample_data_used: * @sampledata: Sample data object * * Increment use count of a sample data object. Use references are not the same as object references and * in contrast indicate if a sample data object should continue to be a part of the sample data pool - if it is * a dependency of an active loaded instrument for example. * * Since: 1.1.0 */ void ipatch_sample_data_used(IpatchSampleData *sampledata) { g_return_if_fail(IPATCH_IS_SAMPLE_DATA(sampledata)); if(g_atomic_int_exchange_and_add(&sampledata->usecount, 1) == 0) { // add to the master list G_LOCK(sample_data_list); sample_data_list = g_slist_prepend(sample_data_list, sampledata); G_UNLOCK(sample_data_list); } } /** * ipatch_sample_data_unused: * @sampledata: Sample data object * * Decrement use count of a sample data object. When the use count drops to 0 it will be removed from the * pool of active sample data objects. Sample data object is only freed after it's reference count goes to 0 though. * * Since: 1.1.0 */ void ipatch_sample_data_unused(IpatchSampleData *sampledata) { g_return_if_fail(IPATCH_IS_SAMPLE_DATA(sampledata)); if(g_atomic_int_dec_and_test(&sampledata->usecount)) { // remove from master list G_LOCK(sample_data_list); sample_data_list = g_slist_remove(sample_data_list, sampledata); G_UNLOCK(sample_data_list); } } /** * ipatch_sample_data_add: * @sampledata: Sample data object * @store: Sample store to add * * Add a sample to a sample data object. If no samples have yet been added, * then the added sample becomes the native sample. All samples added to a * given @sampledata object must have the same frame count and should also * have the same sample rate. This is not enforced though and is left to the * caller to ensure. */ void ipatch_sample_data_add(IpatchSampleData *sampledata, IpatchSampleStore *store) { g_return_if_fail(IPATCH_IS_SAMPLE_DATA(sampledata)); g_return_if_fail(IPATCH_IS_SAMPLE_STORE(store)); g_object_ref(store); /* ++ ref sample for sampledata object */ /* IpatchSampleData not really a container, just set the store's parent directly */ IPATCH_ITEM_WLOCK(store); IPATCH_ITEM(store)->parent = IPATCH_ITEM(sampledata); IPATCH_ITEM_WUNLOCK(store); IPATCH_ITEM_WLOCK(sampledata); sampledata->samples = g_slist_append(sampledata->samples, store); IPATCH_ITEM_WUNLOCK(sampledata); } /** * ipatch_sample_data_remove: * @sampledata: Sample data object * @store: Sample store to remove * * Remove a sample from a sample data object. The native sample should not * be removed from an active sample data object. Use * ipatch_sample_data_replace_native_sample() if replacement is desired. */ void ipatch_sample_data_remove(IpatchSampleData *sampledata, IpatchSampleStore *store) { GSList *p, *prev = NULL; g_return_if_fail(IPATCH_IS_SAMPLE_DATA(sampledata)); g_return_if_fail(IPATCH_IS_SAMPLE_STORE(store)); IPATCH_ITEM_WLOCK(sampledata); for(p = sampledata->samples; p; prev = p, p = p->next) { if(p->data == store) { if(prev) { prev->next = p->next; } else { sampledata->samples = p->next; } break; } } IPATCH_ITEM_WUNLOCK(sampledata); if(p) { ipatch_sample_data_release_store(store); // -- release store g_slist_free_1(p); } } /* Release a sample store, by clearing its parent sample data pointer, * updating cache metrics (if its an #IpatchSampleStoreCache) and unrefing it */ static void ipatch_sample_data_release_store(IpatchSampleStore *store) { guint size_bytes; if(IPATCH_IS_SAMPLE_STORE_CACHE(store)) { size_bytes = ipatch_sample_store_get_size_bytes(store); IPATCH_ITEM_RLOCK(store); /* ++ lock store */ /* Recursive lock: store, sample_cache_vars */ G_LOCK(sample_cache_vars); sample_cache_total_size -= size_bytes; /* Only subtract unused size from total unused size, if no opens active */ if(((IpatchSampleStoreCache *)store)->open_count == 0) { sample_cache_unused_size -= size_bytes; } G_UNLOCK(sample_cache_vars); IPATCH_ITEM_RUNLOCK(store); /* -- unlock store */ } /* IpatchSampleData not really a container, just unset the store's parent directly */ IPATCH_ITEM_WLOCK(store); IPATCH_ITEM(store)->parent = NULL; IPATCH_ITEM_WUNLOCK(store); g_object_unref(store); /* -- unref sample store */ } /** * ipatch_sample_data_replace_native_sample: * @sampledata: Sample data object * @store: Sample store object * * Replace the native sample of a sample data object. This function can be used * even if there are no samples yet, in which case it behaves just like * ipatch_sample_data_add(). * * The @store object can already be added to @sampledata, does nothing if already native sample * (libInstPatch version 1.1.0+). */ void ipatch_sample_data_replace_native_sample(IpatchSampleData *sampledata, IpatchSampleStore *store) { IpatchSampleStore *oldsample = NULL; IpatchItem *store_item = (IpatchItem *)store; IpatchItem *sampledata_item = (IpatchItem *)sampledata; gboolean already_added = FALSE; GSList *p, *prev, *oldlistitem = NULL; g_return_if_fail(IPATCH_IS_SAMPLE_DATA(sampledata)); g_return_if_fail(IPATCH_IS_SAMPLE_STORE(store)); /* IpatchSampleData not really a container, just set the store's parent directly */ IPATCH_ITEM_WLOCK(store); if(log_if_fail(!store_item->parent || store_item->parent == sampledata_item)) { IPATCH_ITEM_WUNLOCK(store); return; } already_added = store_item->parent == sampledata_item; store_item->parent = (IpatchItem *)sampledata; IPATCH_ITEM_WUNLOCK(store); IPATCH_ITEM_WLOCK(sampledata); if(already_added) { for(p = sampledata->samples, prev = NULL; p; prev = p, p = p->next) { if(((IpatchSampleStore *)(p->data)) == store) { if(p == sampledata->samples) // Do nothing if sample store is already the native sample { IPATCH_ITEM_WUNLOCK(sampledata); return; } oldlistitem = p; prev->next = p->next; } } } if(sampledata->samples) { oldsample = sampledata->samples->data; sampledata->samples->data = store; } else { sampledata->samples = g_slist_prepend(sampledata->samples, store); } IPATCH_ITEM_WUNLOCK(sampledata); // Only ref store if not already added to sampledata if(!oldlistitem) { g_object_ref(store); /* ++ ref sample for sampledata */ } if(oldsample) { ipatch_sample_data_release_store(oldsample); // -- release store } if(oldlistitem) { g_slist_free_1(oldlistitem); } } /** * ipatch_sample_data_get_samples: * @sampledata: Sample data object * * Get an object list of samples in a sample data object. The first sample is * the native sample. * * Returns: (transfer full): Newly created list of #IpatchSampleStore objects with a refcount of * 1 which the caller owns. */ IpatchList * ipatch_sample_data_get_samples(IpatchSampleData *sampledata) { IpatchList *list; GSList *p; g_return_val_if_fail(IPATCH_IS_SAMPLE_DATA(sampledata), NULL); list = ipatch_list_new(); /* ++ ref new list */ IPATCH_ITEM_RLOCK(sampledata); for(p = sampledata->samples; p; p = p->next) { g_object_ref(p->data); /* ++ ref object for list */ list->items = g_list_prepend(list->items, p->data); /* Prepend for speed */ } IPATCH_ITEM_RUNLOCK(sampledata); list->items = g_list_reverse(list->items); /* Correct for prepend operation */ return (list); /* !! caller takes over reference */ } /** * ipatch_sample_data_get_size: * @sampledata: Sample data to get size of * * Get the size in frames of the samples in the @sampledata object. * * Returns: Size in frames of stores in sample data. */ guint ipatch_sample_data_get_size(IpatchSampleData *sampledata) { guint size = 0; g_return_val_if_fail(IPATCH_IS_SAMPLE_DATA(sampledata), 0); IPATCH_ITEM_RLOCK(sampledata); if(sampledata->samples) { size = ipatch_sample_store_get_size((IpatchSampleStore *)(sampledata->samples->data)); } IPATCH_ITEM_RUNLOCK(sampledata); return (size); } /** * ipatch_sample_data_get_native_sample: * @sampledata: Sample data object * * Get the native sample of a sample data object. * * Returns: (transfer full): Native sample, or %NULL if no native sample in the sample data object, * caller owns a reference. */ IpatchSampleStore * ipatch_sample_data_get_native_sample(IpatchSampleData *sampledata) { IpatchSampleStore *sample = NULL; g_return_val_if_fail(IPATCH_IS_SAMPLE_DATA(sampledata), NULL); IPATCH_ITEM_RLOCK(sampledata); if(sampledata->samples) { sample = g_object_ref(sampledata->samples->data); /* ++ ref sample */ } IPATCH_ITEM_RUNLOCK(sampledata); return (sample); /* !! caller takes over ref */ } /** * ipatch_sample_data_get_native_format: * @sampledata: Sample data object * * Convenience function to get the sample format of the native sample in a * sample data object. See ipatch_sample_get_format() for more info. * * Returns: Sample format or 0 if @sampledata has no native sample. */ int ipatch_sample_data_get_native_format(IpatchSampleData *sampledata) { IpatchSampleStore *store; int format = 0; g_return_val_if_fail(IPATCH_IS_SAMPLE_DATA(sampledata), 0); IPATCH_ITEM_RLOCK(sampledata); if(sampledata->samples) { store = sampledata->samples->data; format = ipatch_sample_store_get_format(store); } IPATCH_ITEM_RUNLOCK(sampledata); return (format); } /** * ipatch_sample_data_open_native_sample: * @sampledata: Sample data * @handle: (out): Caller supplied structure to initialize * @mode: Access mode to sample, 'r' for reading and 'w' for writing * @format: Sample format to convert to/from (0 for no conversion or to assign * a transform object with ipatch_sample_handle_set_transform()). * @channel_map: Channel mapping if @format is set (set to 0 otherwise), use * #IPATCH_SAMPLE_UNITY_CHANNEL_MAP for 1 to 1 channel mapping * (see ipatch_sample_get_transform_funcs() for details). * @err: Location to store error information * * A convenience function to open a handle to a @sampledata object's native sample. * See ipatch_sample_handle_open() for more details. This is identical to calling * ipatch_sample_data_get_native_sample() and then ipatch_sample_handle_open() on * the returned sample. * * Returns: %TRUE on success, %FALSE on failure (in which case @err may be set) */ gboolean ipatch_sample_data_open_native_sample(IpatchSampleData *sampledata, IpatchSampleHandle *handle, char mode, int format, guint32 channel_map, GError **err) { IpatchSampleStore *native_sample; gboolean retval; g_return_val_if_fail(IPATCH_IS_SAMPLE_DATA(sampledata), FALSE); native_sample = ipatch_sample_data_get_native_sample(sampledata); // ++ ref sample store g_return_val_if_fail(native_sample != NULL, FALSE); retval = ipatch_sample_handle_open((IpatchSample *)native_sample, handle, mode, format, channel_map, err); g_object_unref(native_sample); // -- unref sample store return (retval); } /** * ipatch_sample_data_get_cache_sample: * @sampledata: Sample data object * @format: Sample format of cached sample to convert native sample to * @channel_map: Channel mapping to use for new cached sample when converting * from native format, use #IPATCH_SAMPLE_UNITY_CHANNEL_MAP for 1 to 1 channel * mapping (see ipatch_sample_get_transform_funcs() for details). * @err: Location to store error information * * Get a cached version, in RAM, of a sample. If an existing cached sample * already exists with the given format and channel map, it is used. Otherwise * a new #IpatchSampleStoreCache sample is created and the native sample is * converted as necessary. If a matching cached sample is currently being * created by another thread, this function will block until it is created and * return it. * * Returns: (transfer full): Cached sample with the given @format for which the caller owns a * reference or %NULL if @sampledata contains no samples or a sample * conversion error occurred (I/O error for example). */ IpatchSampleStore * ipatch_sample_data_get_cache_sample(IpatchSampleData *sampledata, int format, guint32 channel_map, GError **err) { IpatchSampleStore *store; IpatchSample *c_sample; guint size_bytes; GSList *p, *prev = NULL; int src_format; guint32 maskval, src_channel_map; CachingInfo *cinfo = NULL; /* Silence gcc (why?) */ CachingInfo *new_cinfo = NULL; int rate; int i; g_return_val_if_fail(IPATCH_IS_SAMPLE_DATA(sampledata), NULL); g_return_val_if_fail(!err || !*err, NULL); /* Mask the channel_map by the number of channels in format */ for(i = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format), maskval = 0; i > 0; i--) { maskval |= 0x7 << ((i - 1) * 3); } channel_map &= maskval; try_again: IPATCH_ITEM_WLOCK(sampledata); for(p = sampledata->samples; p; p = p->next) { store = (IpatchSampleStore *)(p->data); if(!IPATCH_IS_SAMPLE_STORE_CACHE(store)) { continue; } src_format = ipatch_sample_store_get_format(store); src_channel_map = ipatch_sample_store_cache_get_channel_map((IpatchSampleStoreCache *)store); if(format == src_format && channel_map == src_channel_map) { break; } } if(!p) { if(sampledata->samples) { store = sampledata->samples->data; } else { store = NULL; } } if(store) { g_object_ref(store); /* ++ ref the sample */ } IPATCH_ITEM_WUNLOCK(sampledata); /* Sample already cached or no samples in sample data object? - Return it or NULL */ if(p || !store) { if(new_cinfo) { g_slice_free(CachingInfo, new_cinfo); } return (store); /* !! caller takes over reference */ } src_format = ipatch_sample_store_get_format(store); g_return_val_if_fail(ipatch_sample_format_transform_verify(src_format, format, channel_map), NULL); if(!new_cinfo) { new_cinfo = g_slice_new(CachingInfo); } /* Check if another thread is currently caching the same sample */ g_mutex_lock(caching_mutex); for(p = caching_list; p; p = p->next) { cinfo = p->data; if(cinfo->store == store && cinfo->format == format && cinfo->channel_map == channel_map) { break; } } if(p) /* Matching cache operation in progress? */ { g_cond_wait(caching_cond, caching_mutex); g_mutex_unlock(caching_mutex); goto try_again; } /* No matching active cache in progress.. - Add it */ new_cinfo->store = store; new_cinfo->format = format; new_cinfo->channel_map = channel_map; caching_list = g_slist_prepend(caching_list, new_cinfo); g_mutex_unlock(caching_mutex); /* Cache the sample outside of lock */ g_object_get(store, "sample-size", &size_bytes, "sample-rate", &rate, NULL); size_bytes *= ipatch_sample_format_size(format); /* Add to sample_cache_total_size and sample_cache_unused_size too. Do this * before ipatch_sample_copy_below() since it modifies sample_cache_unused_size */ G_LOCK(sample_cache_vars); sample_cache_total_size += size_bytes; sample_cache_unused_size += size_bytes; G_UNLOCK(sample_cache_vars); c_sample = ipatch_sample_store_cache_new(NULL); /* ++ ref new sample */ g_object_set(c_sample, "sample-format", format, "sample-rate", rate, NULL); ((IpatchSampleStoreCache *)c_sample)->channel_map = channel_map; if(!ipatch_sample_copy(c_sample, (IpatchSample *)store, channel_map, err)) { g_object_unref(c_sample); /* -- unref new sample */ g_object_unref(store); /* -- unref native sample */ c_sample = NULL; goto caching_err; } g_object_unref(store); /* -- unref the native sample */ ipatch_sample_get_size(c_sample, &size_bytes); /* There is a chance that a sample could have been cached by another thread, but this * is unlikely and would just lead to a duplicate cached sample which would * eventually get removed. For the sake of performance we leave out a check * for this. */ g_object_ref(c_sample); /* ++ ref sample for sampledata */ /* IpatchSampleData not really a container, just set the store's parent directly */ IPATCH_ITEM(store)->parent = IPATCH_ITEM(sampledata); IPATCH_ITEM_WLOCK(sampledata); sampledata->samples = g_slist_append(sampledata->samples, c_sample); IPATCH_ITEM_WUNLOCK(sampledata); caching_err: /* If caching operation fails, make sure we remove CachingInfo from list */ g_mutex_lock(caching_mutex); for(p = caching_list; p; prev = p, p = p->next) { cinfo = p->data; if(cinfo->store == store && cinfo->format == format && cinfo->channel_map == channel_map) { if(prev) { prev->next = p->next; } else { caching_list = p->next; } break; } } g_mutex_unlock(caching_mutex); g_slice_free(CachingInfo, cinfo); g_slist_free1(p); return ((IpatchSampleStore *)c_sample); /* !! caller takes over reference */ } /** * ipatch_sample_data_lookup_cache_sample: * @sampledata: Sample data object * @format: Sample format * @channel_map: Channel mapping of cached sample relative to native sample format. * * Like ipatch_sample_data_get_cache_sample() but does not create a new cache * sample if it doesn't exist. * * Returns: (transfer full): Cached sample store with the given @format and @channel_map for * which the caller owns a reference or %NULL if @sampledata does not contain * a matching cached sample. */ IpatchSampleStore * ipatch_sample_data_lookup_cache_sample(IpatchSampleData *sampledata, int format, guint32 channel_map) { IpatchSampleStore *store; int src_format; guint32 maskval, src_channel_map; GSList *p; int i; g_return_val_if_fail(IPATCH_IS_SAMPLE_DATA(sampledata), NULL); g_return_val_if_fail(ipatch_sample_format_verify(format), NULL); /* Mask the channel_map by the number of channels in format */ for(i = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format), maskval = 0; i > 0; i--) { maskval |= 0x7 << ((i - 1) * 3); } channel_map &= ~maskval; IPATCH_ITEM_WLOCK(sampledata); for(p = sampledata->samples; p; p = p->next) { store = (IpatchSampleStore *)(p->data); if(!IPATCH_IS_SAMPLE_STORE_CACHE(store)) { continue; } src_format = ipatch_sample_store_get_format(store); src_channel_map = ipatch_sample_store_cache_get_channel_map((IpatchSampleStoreCache *)store); if(format == src_format && channel_map == src_channel_map) { g_object_ref(store); /* ++ ref sample */ break; } } IPATCH_ITEM_WUNLOCK(sampledata); return (p ? store : NULL); /* !! caller takes over reference */ } /** * ipatch_sample_data_open_cache_sample: * @sampledata: Sample data object * @handle: Caller supplied sample handle to initialize * @format: Sample format * @err: Location to store error information * * Like ipatch_sample_data_get_cache_sample() but opens the resulting cached * sample as a convenience. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_sample_data_open_cache_sample(IpatchSampleData *sampledata, IpatchSampleHandle *handle, int format, guint32 channel_map, GError **err) { IpatchSampleStore *store; gboolean retval; g_return_val_if_fail(IPATCH_IS_SAMPLE_DATA(sampledata), FALSE); g_return_val_if_fail(handle != NULL, FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* ++ ref store */ store = ipatch_sample_data_get_cache_sample(sampledata, format, channel_map, err); if(!store) { return (FALSE); } retval = ipatch_sample_handle_open(IPATCH_SAMPLE(store), handle, 'r', format, IPATCH_SAMPLE_UNITY_CHANNEL_MAP, err); g_object_unref(store); /* -- unref store */ return (retval); } /** * ipatch_sample_cache_get_unused_size: * * Get the current unused sample cache data size. * * Returns: Total amount of unused sample cache data in RAM. * * Since: 1.1.0 */ guint64 ipatch_sample_cache_get_unused_size(void) { guint64 cur_unused_size; G_LOCK(sample_cache_vars); /* ++ lock sample data list and variables */ cur_unused_size = sample_cache_unused_size; G_UNLOCK(sample_cache_vars); /* -- unlock sample data list and variables */ return (cur_unused_size); } /** * ipatch_sample_data_cache_clean: * @max_unused_size: Maximum unused cached sample data size (0 to remove all unused samples) * @max_unused_age: Maximum age of unused cached samples in seconds (0 to disable * time based removal), this is the age since they were last in an open state. * * Should be called periodically to release unused cached samples by size and/or * age criteria. */ void ipatch_sample_cache_clean(guint64 max_unused_size, guint max_unused_age) { IpatchSampleData *sampledata; IpatchSampleStoreCache *store; guint64 cur_unused_size; GSList *unused = NULL; GTimeVal time; glong last_open; GSList *p, *p2; if(max_unused_age != 0) { g_get_current_time(&time); } G_LOCK(sample_cache_vars); /* ++ lock sample data list and variables */ /* Optimize case of no unused samples or not removing by age and unused size does * not exceed max_unused_size */ if(sample_cache_unused_size == 0 || (max_unused_age == 0 && sample_cache_unused_size <= max_unused_size)) { G_UNLOCK(sample_cache_vars); /* -- unlock sample data list and variables */ return; } G_UNLOCK(sample_cache_vars); /* -- unlock sample data list and variables */ G_LOCK(sample_data_list); /* Lock sample data list */ for(p = sample_data_list; p; p = p->next) { sampledata = (IpatchSampleData *)(p->data); IPATCH_ITEM_RLOCK(sampledata); for(p2 = sampledata->samples; p2; p2 = p2->next) { store = (IpatchSampleStoreCache *)(p2->data); if(IPATCH_IS_SAMPLE_STORE_CACHE(store) && ipatch_sample_store_cache_get_open_count(store) == 0) { unused = g_slist_prepend(unused, g_object_ref(store)); /* ++ ref store for list */ g_object_ref(sampledata); /* ++ ref sample data, to guarantee its existence out of lock */ } } IPATCH_ITEM_RUNLOCK(sampledata); } G_UNLOCK(sample_data_list); /* -- unlock sample data list */ /* Sort list by last open age (oldest first) */ unused = g_slist_sort(unused, sample_cache_clean_sort); /* Free samples until criteria no longer matches */ for(p = unused; p; p = p->next) { store = (IpatchSampleStoreCache *)(p->data); IPATCH_ITEM_RLOCK(store); last_open = store->last_open; IPATCH_ITEM_RUNLOCK(store); sampledata = (IpatchSampleData *)(((IpatchItem *)store)->parent); if(last_open == 0) { g_object_unref(sampledata); g_object_unref(store); continue; /* Store got opened since it was added to list? */ } G_LOCK(sample_cache_vars); cur_unused_size = sample_cache_unused_size; G_UNLOCK(sample_cache_vars); /* Once size drops below max_unused_size and max_unused_age is 0 or this * sample was used more recent than max_unused_age - we're done */ if(cur_unused_size <= max_unused_size && (max_unused_age == 0 || time.tv_sec - last_open <= (glong)max_unused_age)) { break; } ipatch_sample_data_remove(sampledata, (IpatchSampleStore *)store); g_object_unref(sampledata); /* -- unref sample data from list */ g_object_unref(store); /* -- unref sample store from list */ } g_slist_free(unused); /* -- free list */ } /* Sort list of unused items by age (oldest to newest) */ static gint sample_cache_clean_sort(gconstpointer a, gconstpointer b) { const IpatchSampleStoreCache *astore = a, *bstore = b; glong alast_open, blast_open; IPATCH_ITEM_RLOCK(astore); alast_open = astore->last_open; IPATCH_ITEM_RUNLOCK(astore); IPATCH_ITEM_RLOCK(bstore); blast_open = bstore->last_open; IPATCH_ITEM_RUNLOCK(bstore); /* In case store got opened since add to list */ if(alast_open == 0) { return (1); } if(blast_open == 0) { return (-1); } if(alast_open < blast_open) { return (-1); } if(alast_open > blast_open) { return (1); } return (0); } /** * ipatch_sample_data_get_blank: * * Get blank sample data object. Return's a sample data structure * with the minimum amount of data which is blank. Only creates it on * the first call, subsequent calls return the same sample data * object. Therefore it should not be modified. The blank sample data's * reference count has been incremented and should be removed by the * caller with g_object_unref() when finished with it. * * Returns: (transfer full): The blank sample data object. Remember to unref it when not * using it anymore with g_object_unref(). */ IpatchSampleData * ipatch_sample_data_get_blank(void) { static IpatchSampleData *blank_sampledata = NULL; IpatchSample *sample; if(!blank_sampledata) /* blank sampledata already created? */ { blank_sampledata = ipatch_sample_data_new(); /* ++ ref new item */ g_object_ref(blank_sampledata); /* ++ ref for static blank_sampledata */ ipatch_sample_data_used(blank_sampledata); // ++ inc use count for static blank_sampledata sample = ipatch_sample_store_ram_get_blank(); ipatch_sample_data_add(blank_sampledata, (IpatchSampleStore *)sample); } else { g_object_ref(blank_sampledata); } return (blank_sampledata); } /** * _ipatch_sample_data_cache_add_unused_size: (skip) * * Function used by IpatchSampleStoreCache.c */ void _ipatch_sample_data_cache_add_unused_size(int size) { G_LOCK(sample_cache_vars); sample_cache_unused_size += size; G_UNLOCK(sample_cache_vars); } #ifdef IPATCH_DEBUG /** * ipatch_sample_data_dump: (skip) * * Dump sample data information to stdout for debugging purposes. */ void ipatch_sample_data_dump(void) { IpatchList *data_list; IpatchList *store_list; IpatchSampleStore *store; GList *pdata, *pstore; data_list = ipatch_get_sample_data_list(); // ++ ref sample data list for(pdata = data_list->items; pdata; pdata = pdata->next) { store_list = ipatch_sample_data_get_samples(IPATCH_SAMPLE_DATA(pdata->data)); // ++ ref stores list printf("Data %p: samples=%d\n", pdata->data, g_list_length(store_list->items)); for(pstore = store_list->items; pstore; pstore = pstore->next) { int sample_format, sample_rate, loop_type, root_note, fine_tune; guint sample_size, loop_start, loop_end; char *title; store = IPATCH_SAMPLE_STORE(pstore->data); g_object_get(store, "title", &title, "sample-size", &sample_size, // ++ allocate title "sample-format", &sample_format, "sample-rate", &sample_rate, "loop-type", &loop_type, "loop-start", &loop_start, "loop-end", &loop_end, "root-note", &root_note, "fine-tune", &fine_tune, NULL); printf(" Store %p (%s): title='%s' size=%u fmt=0x%X rate=%d ltype=%d lstart=%u lend=%u root=%d fine=%d\n", store, g_type_name(G_OBJECT_TYPE(store)), title, sample_size, sample_format, sample_rate, loop_type, loop_start, loop_end, root_note, fine_tune); g_free(title); // -- free title // Show some details about file stores if(IPATCH_IS_SAMPLE_STORE_FILE(store)) { IpatchFile *file; char *filename = NULL; guint location; g_object_get(store, "file", &file, "location", &location, NULL); // ++ ref file object if(file) { filename = ipatch_file_get_name(file); // ++ alloc file name } printf(" File %p (%s): filename='%s' location=%u\n", file, g_type_name(G_OBJECT_TYPE(file)), filename, location); g_free(filename); // -- free file name g_object_unref(file); // -- unref file } // Show details about swap stores else if(IPATCH_IS_SAMPLE_STORE_SWAP(store)) { IpatchSampleStoreSwap *swap_store = (IpatchSampleStoreSwap *)store; if(swap_store->ram_location) { printf(" RAM %p\n", swap_store->ram_location); } else { printf(" Swap %u\n", swap_store->location); } } } g_object_unref(store_list); // -- unref stores list } g_object_unref(data_list); // -- unref sample data list } #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleData.h000066400000000000000000000122671400263525300222140ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SAMPLE_DATA_H__ #define __IPATCH_SAMPLE_DATA_H__ #include #include #include /* forward type declarations */ typedef struct _IpatchSampleData IpatchSampleData; typedef struct _IpatchSampleDataClass IpatchSampleDataClass; #include #include #include #define IPATCH_TYPE_SAMPLE_DATA (ipatch_sample_data_get_type ()) #define IPATCH_SAMPLE_DATA(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SAMPLE_DATA, IpatchSampleData)) #define IPATCH_SAMPLE_DATA_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SAMPLE_DATA, IpatchSampleDataClass)) #define IPATCH_IS_SAMPLE_DATA(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SAMPLE_DATA)) #define IPATCH_IS_SAMPLE_DATA_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SAMPLE_DATA)) #define IPATCH_SAMPLE_DATA_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SAMPLE_DATA, IpatchSampleDataClass)) /* Sample data instance */ struct _IpatchSampleData { IpatchItem parent_instance; GSList *samples; /* list of IpatchSampleStore (1st is native sample) */ int usecount; /* Count of users of this sample data object */ }; struct _IpatchSampleDataClass { IpatchItemClass parent_class; }; /** * IPATCH_SAMPLE_DATA_UNUSED_FLAG_SHIFT: (skip) */ /* Reserve 3 item flags for expansion */ #define IPATCH_SAMPLE_DATA_UNUSED_FLAG_SHIFT \ (IPATCH_CONTAINER_UNUSED_FLAG_SHIFT + 3) /** * IpatchSampleDataMigrateFlags: * @IPATCH_SAMPLE_DATA_MIGRATE_REMOVE_NEW_IF_UNUSED: Remove unused #IpatchSampleStore objects referencing newfile. * @IPATCH_SAMPLE_DATA_MIGRATE_TO_NEWFILE: Migrate all #IpatchSampleData objects to newfile which have a #IpatchSampleStore * with the same format as the native format therein, default is to only migrate those with their native samples in * oldfile or swap storage. * @IPATCH_SAMPLE_DATA_MIGRATE_LEAVE_IN_SWAP: Leave native samples in #IpatchSampleData objects in swap, even if present in * newfile, default is to migrate samples out of swap in preference for newfile. * @IPATCH_SAMPLE_DATA_MIGRATE_REPLACE: Replace oldfile with newfile (has no effect if newfile is %NULL) * * Since: 1.1.0 */ typedef enum { IPATCH_SAMPLE_DATA_MIGRATE_REMOVE_NEW_IF_UNUSED = 1 << 0, IPATCH_SAMPLE_DATA_MIGRATE_TO_NEWFILE = 1 << 1, IPATCH_SAMPLE_DATA_MIGRATE_LEAVE_IN_SWAP = 1 << 2, IPATCH_SAMPLE_DATA_MIGRATE_REPLACE = 1 << 3 } IpatchSampleDataMigrateFlags; IpatchList *ipatch_get_sample_data_list(void); gboolean ipatch_migrate_file_sample_data(IpatchFile *oldfile, IpatchFile *newfile, const char *filename, guint flags, GError **err); GType ipatch_sample_data_get_type(void); IpatchSampleData *ipatch_sample_data_new(void); void ipatch_sample_data_used(IpatchSampleData *sampledata); void ipatch_sample_data_unused(IpatchSampleData *sampledata); void ipatch_sample_data_add(IpatchSampleData *sampledata, IpatchSampleStore *store); void ipatch_sample_data_remove(IpatchSampleData *sampledata, IpatchSampleStore *store); void ipatch_sample_data_replace_native_sample(IpatchSampleData *sampledata, IpatchSampleStore *store); IpatchList *ipatch_sample_data_get_samples(IpatchSampleData *sampledata); guint ipatch_sample_data_get_size(IpatchSampleData *sampledata); IpatchSampleStore *ipatch_sample_data_get_native_sample(IpatchSampleData *sampledata); int ipatch_sample_data_get_native_format(IpatchSampleData *sampledata); gboolean ipatch_sample_data_open_native_sample(IpatchSampleData *sampledata, IpatchSampleHandle *handle, char mode, int format, guint32 channel_map, GError **err); IpatchSampleStore *ipatch_sample_data_get_cache_sample(IpatchSampleData *sampledata, int format, guint32 channel_map, GError **err); IpatchSampleStore *ipatch_sample_data_lookup_cache_sample(IpatchSampleData *sampledata, int format, guint32 channel_map); gboolean ipatch_sample_data_open_cache_sample(IpatchSampleData *sampledata, IpatchSampleHandle *handle, int format, guint32 channel_map, GError **err); guint64 ipatch_sample_cache_get_unused_size(void); void ipatch_sample_cache_clean(guint64 max_unused_size, guint max_unused_age); IpatchSampleData *ipatch_sample_data_get_blank(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleList.c000066400000000000000000000446301400263525300222500ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSampleList * @short_description: Sample list data types and functions * @see_also: * @stability: Stable * * Sample lists define audio data from concatenated segments of other * audio sources. The lists are always mono (a single channel can be * selected from multi-channel sources). Multi-channel audio can be * created from combining multiple sample lists of the same length. */ #include "IpatchSampleList.h" #include "ipatch_priv.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif static GList *list_item_free_next(IpatchSampleList *list, GList *itemp); GType ipatch_sample_list_get_type(void) { static GType type = 0; if(!type) type = g_boxed_type_register_static("IpatchSampleList", (GBoxedCopyFunc)ipatch_sample_list_duplicate, (GBoxedFreeFunc)ipatch_sample_list_free); return (type); } GType ipatch_sample_list_item_get_type(void) { static GType type = 0; if(!type) type = g_boxed_type_register_static("IpatchSampleListItem", (GBoxedCopyFunc)ipatch_sample_list_item_duplicate, (GBoxedFreeFunc)ipatch_sample_list_item_free); return (type); } /** * ipatch_sample_list_new: * * Creates a new empty (zero item terminated) sample list. * * Returns: Newly allocated empty sample list. */ IpatchSampleList * ipatch_sample_list_new(void) { return (g_slice_new0(IpatchSampleList)); } /** * ipatch_sample_list_free: * @list: Sample list to free * * Free a sample list. */ void ipatch_sample_list_free(IpatchSampleList *list) { GList *p; g_return_if_fail(list != NULL); /* free the items in the list */ for(p = list->items; p; p = g_list_delete_link(p, p)) { ipatch_sample_list_item_free((IpatchSampleListItem *)(p->data)); } g_slice_free(IpatchSampleList, list); } /** * ipatch_sample_list_duplicate: * @list: Sample list to duplicate * * Duplicate a sample list. * * Returns: Newly allocated duplicate of @list. */ IpatchSampleList * ipatch_sample_list_duplicate(IpatchSampleList *list) { IpatchSampleListItem *item; IpatchSampleList *newlist; GList *p; g_return_val_if_fail(list != NULL, NULL); newlist = ipatch_sample_list_new(); newlist->total_size = list->total_size; for(p = list->items; p; p = p->next) { item = ipatch_sample_list_item_duplicate((IpatchSampleListItem *)(p->data)); newlist->items = g_list_prepend(newlist->items, item); } newlist->items = g_list_reverse(newlist->items); return (newlist); } /** * ipatch_sample_list_item_new: * * Create a new node for a sample list. * * Returns: Newly allocated list node which should be freed with * ipatch_sample_list_item_free() when finished using it. */ IpatchSampleListItem * ipatch_sample_list_item_new(void) { return (g_slice_new0(IpatchSampleListItem)); } /** * ipatch_sample_list_item_new_init: * @sample: Sample containing audio for the segment * @ofs: Offset in @sample of audio segment * @size: Size of audio segment in frames * @channel: Channel to use from @sample * * Create a new sample list item and initialize it with the provided parameters. * * Returns: Newly allocated sample list item which should be freed with * ipatch_sample_list_item_free() when done with it. */ IpatchSampleListItem * ipatch_sample_list_item_new_init(IpatchSample *sample, guint ofs, guint size, guint channel) { IpatchSampleListItem *item; guint sample_size; g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), NULL); g_return_val_if_fail(size > 0, NULL); sample_size = ipatch_sample_get_size(sample, NULL); g_return_val_if_fail(ofs + size <= sample_size, NULL); item = ipatch_sample_list_item_new(); item->sample = g_object_ref(sample); item->size = size; item->ofs = ofs; item->channel = channel; return (item); } /** * ipatch_sample_list_item_free: * @item: Sample list item to free * * Free a sample list item previously allocated by ipatch_sample_list_item_new(). */ void ipatch_sample_list_item_free(IpatchSampleListItem *item) { if(item->sample) { g_object_unref(item->sample); } g_slice_free(IpatchSampleListItem, item); } /** * ipatch_sample_list_item_duplicate: * @item: Sample list item to duplicate * * Duplicate a sample list item node. * * Returns: New duplicate sample list item. */ IpatchSampleListItem * ipatch_sample_list_item_duplicate(IpatchSampleListItem *item) { IpatchSampleListItem *newitem; newitem = ipatch_sample_list_item_new(); newitem->sample = item->sample ? g_object_ref(item->sample) : NULL; newitem->ofs = item->ofs; newitem->size = item->size; newitem->channel = item->channel; return (newitem); } /** * ipatch_sample_list_append: * @list: Sample list * @sample: Sample containing audio segment to append * @ofs: Offset in @sample of beginning of audio segment (in frames) * @size: Number of frames of audio segment to append * @channel: Channel to use from @sample * * Append an audio segment to a sample list. */ void ipatch_sample_list_append(IpatchSampleList *list, IpatchSample *sample, guint ofs, guint size, guint channel) { IpatchSampleListItem *item; g_return_if_fail(list != NULL); item = ipatch_sample_list_item_new_init(sample, ofs, size, channel); g_return_if_fail(item != NULL); list->items = g_list_append(list->items, item); list->total_size += size; } /** * ipatch_sample_list_prepend: * @list: Sample list * @sample: Sample containing audio segment to prepend * @ofs: Offset in @sample of beginning of audio segment (in frames) * @size: Number of frames of audio segment to prepend * @channel: Channel to use from @sample * * Prepend an audio segment to a sample list. */ void ipatch_sample_list_prepend(IpatchSampleList *list, IpatchSample *sample, guint ofs, guint size, guint channel) { IpatchSampleListItem *item; g_return_if_fail(list != NULL); item = ipatch_sample_list_item_new_init(sample, ofs, size, channel); g_return_if_fail(item != NULL); list->items = g_list_prepend(list->items, item); list->total_size += size; } /** * ipatch_sample_list_insert_index: * @list: Sample list * @index: List index to insert segment before (0 = prepend, -1 = append) * @sample: Sample containing audio segment to insert * @ofs: Offset in @sample of beginning of audio segment (in frames) * @size: Number of frames of audio segment to insert * @channel: Channel to use from @sample * * Insert an audio segment into a sample list before a given list segment * @index. */ void ipatch_sample_list_insert_index(IpatchSampleList *list, guint index, IpatchSample *sample, guint ofs, guint size, guint channel) { IpatchSampleListItem *item; g_return_if_fail(list != NULL); item = ipatch_sample_list_item_new_init(sample, ofs, size, channel); g_return_if_fail(item != NULL); list->items = g_list_insert(list->items, item, index); list->total_size += size; } /** * ipatch_sample_list_insert: * @list: Sample list * @pos: Position in audio data in frames to insert segment at * @sample: Sample containing audio segment to insert * @ofs: Offset in @sample of beginning of audio segment (in frames) * @size: Number of frames of audio segment to insert * @channel: Channel to use from @sample * * Insert an audio segment into a sample list at a given sample position * in frames (@pos). Existing segments will be split as needed to accomodate * the inserted segment. */ void ipatch_sample_list_insert(IpatchSampleList *list, guint pos, IpatchSample *sample, guint ofs, guint size, guint channel) { IpatchSampleListItem *item, *newitem, *splititem; guint startofs, sz; GList *p; g_return_if_fail(list != NULL); g_return_if_fail(pos <= list->total_size); newitem = ipatch_sample_list_item_new_init(sample, ofs, size, channel); g_return_if_fail(newitem != NULL); /* search for segment containing pos */ for(p = list->items, startofs = 0; p; p = p->next) { item = (IpatchSampleListItem *)(p->data); if(pos >= startofs && pos < (startofs + item->size)) { break; } startofs += item->size; } /* if list was exhausted, it means pos is past the end - append */ if(!p) { list->items = g_list_append(list->items, newitem); } else if(pos != startofs) /* need to split segment? */ { sz = pos - startofs; /* new size of first split segment */ /* create 2nd split segment */ splititem = ipatch_sample_list_item_new_init(item->sample, item->ofs + sz, item->size - sz, item->channel); item->size = sz; /* insert new item after 1st split segment (assign to suppress GCC warning) */ p = g_list_insert(p, newitem, 1); /* insert 2nd split segment */ p = g_list_insert(p, splititem, 2); } /* position is at the beginning of another segment, insert before */ else { list->items = g_list_insert_before(list->items, p, newitem); } list->total_size += size; } /** * ipatch_sample_list_cut: * @list: Sample list * @pos: Start position of sample data to cut, in frames * @size: Size of area to cut, in frames * * Cut a segment of audio from a sample list. */ void ipatch_sample_list_cut(IpatchSampleList *list, guint pos, guint size) { IpatchSampleListItem *item = NULL, *newitem; guint startofs, fsegsize; GList *p; g_return_if_fail(list != NULL); g_return_if_fail(pos + size <= list->total_size); list->total_size -= size; /* search for segment containing pos */ for(p = list->items, startofs = 0; p; p = p->next) { item = (IpatchSampleListItem *)(p->data); if(pos >= startofs && pos < (startofs + item->size)) { break; } startofs += item->size; } g_return_if_fail(p != NULL); /* means total_size is out of sync! */ if(pos == startofs) /* position is at start of segment? */ { if(size < item->size) /* size is less than segment size? */ { /* increase segment offset and decrease size, we are done */ item->ofs += size; item->size -= size; return; } /* adjust size for remaining after segment removed */ size -= item->size; /* !! must be before item delete! */ /* size is greater or equal to segment - remove segment and advance p */ p = list_item_free_next(list, p); if(size == 0) { return; /* only this segment cut? - done */ } } else if(size < item->size - (pos - startofs)) /* cut within segment? */ { fsegsize = pos - startofs; /* size of first segment before cut */ /* create new segment for audio after cut */ newitem = ipatch_sample_list_item_new_init (item->sample, item->ofs + fsegsize + size, item->size - fsegsize - size, item->channel); item->size = fsegsize; /* insert 2nd seg after the 1st segment (assign to suppress GCC warning) */ p = g_list_insert(p, newitem, 1); return; /* done */ } else /* beginning of cut is within segment and continues into more segments */ { fsegsize = pos - startofs; /* new size of truncated segment */ size -= (item->size - fsegsize); /* update size to remaining after seg */ item->size = fsegsize; /* assign new size to segment */ p = p->next; /* next segment */ startofs += fsegsize; /* startofs of next segment */ } /* search for last segment which is cut, and remove all segments in between */ while(p) { item = (IpatchSampleListItem *)(p->data); if(size < item->size) { break; /* remaining size is within segment? */ } /* remaining cut size includes entire segment - remove it */ size -= item->size; /* !! must be before item delete! */ p = list_item_free_next(list, p); } if(p && size > 0) /* cut goes into this segment? */ { item->ofs += size; /* advance offset to after cut */ item->size -= size; /* decrease segment size by remaining cut size */ } } /* remove the sample item at the given list pointer and return the next item in the list */ static GList * list_item_free_next(IpatchSampleList *list, GList *itemp) { GList *retp; retp = itemp->next; ipatch_sample_list_item_free((IpatchSampleListItem *)(itemp->data)); list->items = g_list_delete_link(list->items, itemp); return (retp); } /** * ipatch_sample_list_render: (skip) * @list: Sample list * @buf: Buffer to store the rendered audio to (should be @frames * bytes_per_frame) * @pos: Position in sample list audio to start from, in frames * @frames: Size of sample data to render in frames * @format: Sample format to render to (must be mono) * @err: Location to store error to or %NULL to ignore * * Copies sample data from a sample list, converting as necessary and storing * to @buf. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_sample_list_render(IpatchSampleList *list, gpointer b, guint pos, guint frames, int format, GError **err) { IpatchSampleListItem *item = NULL; guint startofs, block, format_size; GList *p; guint8 *buf = b; // cannot use void* for pointer arithmetic below g_return_val_if_fail(list != NULL, FALSE); g_return_val_if_fail(ipatch_sample_format_verify(format), FALSE); g_return_val_if_fail(pos + frames <= list->total_size, FALSE); g_return_val_if_fail(buf != NULL, FALSE); g_return_val_if_fail(IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format) == 1, FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* search for segment containing pos */ for(p = list->items, startofs = 0; p; p = p->next) { item = (IpatchSampleListItem *)(p->data); if(pos >= startofs && pos < (startofs + item->size)) { break; } startofs += item->size; } g_return_val_if_fail(p != NULL, FALSE); /* means total_size is out of sync! */ block = item->size - (pos - startofs); /* remaining size of segment */ format_size = ipatch_sample_format_size(format); while(frames > 0 && p) { if(block > frames) { block = frames; } /* Read sample data and transform (if necessary) */ if(!ipatch_sample_read_transform (item->sample, item->ofs + (pos - startofs), block, buf, format, IPATCH_SAMPLE_MAP_CHANNEL(0, item->channel), err)) { return (FALSE); } buf += block * format_size; frames -= block; p = p->next; if(p) { item = (IpatchSampleListItem *)(p->data); block = item->size; startofs += item->size; pos = startofs; } } g_return_val_if_fail(frames == 0, FALSE); /* means total_size is out of sync! */ return (TRUE); } /** * ipatch_sample_list_render_alloc: (rename-to ipatch_sample_list_render) * @list: Sample list * @pos: Position in sample list audio to start from, in frames * @size: Size of sample data to render in bytes (must be a multiple of frame size) * @format: Sample format to render to (must be mono) * @err: Location to store error to or %NULL to ignore * * Copies sample data from a sample list, converting as necessary and returning * an allocated buffer. * * Returns: (array length=size) (element-type guint8) (transfer full): Buffer containing * rendered audio of the requested @format or %NULL on error (in which case @err may be set) * * Since: 1.1.0 */ gpointer ipatch_sample_list_render_alloc(IpatchSampleList *list, guint pos, guint size, int format, GError **err) { gpointer buf; int frame_size; g_return_val_if_fail(size > 0, NULL); frame_size = ipatch_sample_format_size(format); g_return_val_if_fail(size % frame_size == 0, NULL); buf = g_malloc(size); // ++ alloc if(!ipatch_sample_list_render(list, buf, pos, size / frame_size, format, err)) { g_free(buf); // -- free return (NULL); } return (buf); // !! caller takes over buffer } #ifdef IPATCH_DEBUG /** * ipatch_sample_list_dump: (skip) * * For debugging purposes, dumps sample list info to stdout. */ void ipatch_sample_list_dump(IpatchSampleList *list) { IpatchSampleListItem *item; guint startofs = 0; int sample_format; guint sample_size; GList *p; int i = 0; printf("Dump of sample list with %d segments totaling %d frames\n", g_list_length(list->items), list->total_size); for(p = list->items; p; p = p->next, i++) { item = (IpatchSampleListItem *)(p->data); if(item->sample) { sample_format = ipatch_sample_get_format(item->sample); sample_size = ipatch_sample_get_size(item->sample, NULL); } else { sample_format = 0; sample_size = 0; } printf("%02d-%06x size=%d ofs=%d chan=%d sample=(%p format %03x size=%d)\n", i, startofs, item->size, item->ofs, item->channel, item->sample, sample_format, sample_size); startofs += item->size; } } #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleList.h000066400000000000000000000071471400263525300222570ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SAMPLE_LIST_H__ #define __IPATCH_SAMPLE_LIST_H__ #include #include typedef struct _IpatchSampleList IpatchSampleList; typedef struct _IpatchSampleListItem IpatchSampleListItem; #include /* Boxed type for sample list and list item */ #define IPATCH_TYPE_SAMPLE_LIST (ipatch_sample_list_get_type ()) #define IPATCH_TYPE_SAMPLE_LIST_ITEM (ipatch_sample_list_item_get_type ()) /** * IpatchSampleList: * * A sample edit list. Allows for non-destructive sample editing by defining * new audio samples from one or more audio sample segments. */ struct _IpatchSampleList { GList *items; /* list of IpatchSampleListItem structs */ guint total_size; /* total size of audio data in frames */ }; /** * IpatchSampleListItem: * * Defines an audio segment in a #IpatchSampleList. */ struct _IpatchSampleListItem { IpatchSample *sample; /* Sample for this segment */ guint ofs; /* Offset in sample of segment start */ guint size; /* Size in frames of audio segment */ guint32 channel : 3; /* Channel to use in sample */ guint32 reserved : 29; }; GType ipatch_sample_list_get_type(void); GType ipatch_sample_list_item_get_type(void); IpatchSampleList *ipatch_sample_list_new(void); void ipatch_sample_list_free(IpatchSampleList *list); IpatchSampleList *ipatch_sample_list_duplicate(IpatchSampleList *list); IpatchSampleListItem *ipatch_sample_list_item_new(void); IpatchSampleListItem *ipatch_sample_list_item_new_init(IpatchSample *sample, guint ofs, guint size, guint channel); void ipatch_sample_list_item_free(IpatchSampleListItem *item); IpatchSampleListItem *ipatch_sample_list_item_duplicate(IpatchSampleListItem *item); void ipatch_sample_list_append(IpatchSampleList *list, IpatchSample *sample, guint ofs, guint size, guint channel); void ipatch_sample_list_prepend(IpatchSampleList *list, IpatchSample *sample, guint ofs, guint size, guint channel); void ipatch_sample_list_insert_index(IpatchSampleList *list, guint index, IpatchSample *sample, guint ofs, guint size, guint channel); void ipatch_sample_list_insert(IpatchSampleList *list, guint pos, IpatchSample *sample, guint ofs, guint size, guint channel); void ipatch_sample_list_cut(IpatchSampleList *list, guint pos, guint size); gboolean ipatch_sample_list_render(IpatchSampleList *list, gpointer buf, guint pos, guint frames, int format, GError **err); gpointer ipatch_sample_list_render_alloc(IpatchSampleList *list, guint pos, guint size, int format, GError **err); #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleStore.c000066400000000000000000000164761400263525300224400ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSampleStore * @short_description: Abstract sample storage object * @see_also: * @stability: Stable * * Sample stores provide for various storage methods for audio data. * Examples include: #IpatchSampleStoreFile for audio data stored in files * on disk, #IpatchSampleStoreRAM for audio in RAM, #IpatchSampleStoreROM for * samples in ROM of a sound card, etc. */ #include #include #include #include "IpatchSampleStore.h" #include "builtin_enums.h" #include "ipatch_priv.h" enum { PROP_0, PROP_SAMPLE_SIZE, /* size of sample (in frames) */ PROP_SAMPLE_FORMAT, /* Sample format (Width | Channels | Signed | Endian) */ PROP_SAMPLE_RATE, /* sample rate hint */ PROP_SAMPLE_DATA, /* sample data pointer (not supported, just returns NULL) */ /* read only dummy properties to conform to IpatchSample interface */ PROP_LOOP_TYPE, PROP_LOOP_START, PROP_LOOP_END, PROP_ROOT_NOTE, PROP_FINE_TUNE }; static void ipatch_sample_store_sample_iface_init(IpatchSampleIface *iface); static void ipatch_sample_store_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sample_store_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); G_DEFINE_ABSTRACT_TYPE_WITH_CODE(IpatchSampleStore, ipatch_sample_store, IPATCH_TYPE_ITEM, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_sample_store_sample_iface_init)) static void ipatch_sample_store_sample_iface_init(IpatchSampleIface *iface) { } static void ipatch_sample_store_class_init(IpatchSampleStoreClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->get_property = ipatch_sample_store_get_property; item_class->item_set_property = ipatch_sample_store_set_property; ipatch_sample_install_property(obj_class, PROP_SAMPLE_SIZE, "sample-size"); ipatch_sample_install_property(obj_class, PROP_SAMPLE_FORMAT, "sample-format"); ipatch_sample_install_property(obj_class, PROP_SAMPLE_RATE, "sample-rate"); ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_DATA, "sample-data"); /* these are read only properties and just return the default value */ ipatch_sample_install_property_readonly(obj_class, PROP_LOOP_TYPE, "loop-type"); ipatch_sample_install_property_readonly(obj_class, PROP_LOOP_START, "loop-start"); ipatch_sample_install_property_readonly(obj_class, PROP_LOOP_END, "loop-end"); ipatch_sample_install_property_readonly(obj_class, PROP_ROOT_NOTE, "root-note"); ipatch_sample_install_property_readonly(obj_class, PROP_FINE_TUNE, "fine-tune"); } static void ipatch_sample_store_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSampleStore *store = IPATCH_SAMPLE_STORE(object); int format; switch(property_id) { case PROP_SAMPLE_SIZE: /* Lock not required, only set once during creation, prior to MT use */ store->size = g_value_get_uint(value); break; case PROP_SAMPLE_FORMAT: format = g_value_get_int(value) << IPATCH_SAMPLE_STORE_FORMAT_SHIFT; /* Lock not required since this should only be done once, prior to MT use */ ipatch_item_clear_flags(store, IPATCH_SAMPLE_FORMAT_MASK << IPATCH_SAMPLE_STORE_FORMAT_SHIFT); ipatch_item_set_flags(store, format); break; case PROP_SAMPLE_RATE: store->rate = g_value_get_int(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_sample_store_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSampleStore *store = IPATCH_SAMPLE_STORE(object); switch(property_id) { case PROP_SAMPLE_SIZE: /* Lock not required since it should not change, once used in MT context */ g_value_set_uint(value, store->size); break; case PROP_SAMPLE_FORMAT: /* Lock not required since format should never change, once used in MT context */ g_value_set_int(value, ipatch_sample_store_get_format(store)); break; case PROP_SAMPLE_RATE: g_value_set_int(value, store->rate); break; case PROP_SAMPLE_DATA: g_value_set_object(value, NULL); break; case PROP_LOOP_TYPE: g_value_set_enum(value, IPATCH_SAMPLE_LOOP_NONE); break; case PROP_LOOP_START: g_value_set_uint(value, 0); break; case PROP_LOOP_END: g_value_set_uint(value, 0); break; case PROP_ROOT_NOTE: g_value_set_int(value, IPATCH_SAMPLE_ROOT_NOTE_DEFAULT); break; case PROP_FINE_TUNE: g_value_set_int(value, 0); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_sample_store_init(IpatchSampleStore *store) { /* initialize format to default - signed 16 bit mono little endian data */ ipatch_item_set_flags(store, IPATCH_SAMPLE_FORMAT_DEFAULT << IPATCH_SAMPLE_STORE_FORMAT_SHIFT); store->rate = IPATCH_SAMPLE_RATE_DEFAULT; } /** * ipatch_sample_store_first: (skip) * @iter: Patch item iterator containing #IpatchSampleStore items * * Gets the first item in a sample store iterator. A convenience wrapper for * ipatch_iter_first(). * * Returns: The first sample store in @iter or %NULL if empty. */ IpatchSampleStore * ipatch_sample_store_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_SAMPLE_STORE(obj)); } else { return (NULL); } } /** * ipatch_sample_store_next: (skip) * @iter: Patch item iterator containing #IpatchSampleStore items * * Gets the next item in a sample store iterator. A convenience wrapper for * ipatch_iter_next(). * * Returns: The next sample store in @iter or %NULL if at the end of the list. */ IpatchSampleStore * ipatch_sample_store_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_SAMPLE_STORE(obj)); } else { return (NULL); } } libinstpatch-1.1.6/libinstpatch/IpatchSampleStore.h000066400000000000000000000101771400263525300224350ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SAMPLE_STORE_H__ #define __IPATCH_SAMPLE_STORE_H__ /* forward type declarations */ typedef struct _IpatchSampleStore IpatchSampleStore; typedef struct _IpatchSampleStoreClass IpatchSampleStoreClass; #include #include #include #include #include #include #include #define IPATCH_TYPE_SAMPLE_STORE (ipatch_sample_store_get_type ()) #define IPATCH_SAMPLE_STORE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SAMPLE_STORE, \ IpatchSampleStore)) #define IPATCH_SAMPLE_STORE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SAMPLE_STORE, \ IpatchSampleStoreClass)) #define IPATCH_IS_SAMPLE_STORE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SAMPLE_STORE)) #define IPATCH_IS_SAMPLE_STORE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SAMPLE_STORE)) #define IPATCH_SAMPLE_STORE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SAMPLE_STORE, \ IpatchSampleStoreClass)) /* sample store base instance */ struct _IpatchSampleStore { IpatchItem parent_instance; guint32 size; /* size of sample data (in samples) */ guint32 rate; /* sample rate in hz */ }; /* sample store base class */ struct _IpatchSampleStoreClass { IpatchItemClass parent_class; }; /* IpatchSampleWidth | sign | endian | channels stored in flags field (9 bits) - see sample.h */ /* flags bit shift value to sample format field */ #define IPATCH_SAMPLE_STORE_FORMAT_SHIFT IPATCH_ITEM_UNUSED_FLAG_SHIFT /** * ipatch_sample_store_get_format: * * Macro for getting the sample format from a sample store. No lock is required * since format can only be set prior to the store being actively used. * * Returns: Sample format field. See #sample. */ #define ipatch_sample_store_get_format(store) \ ((ipatch_item_get_flags (store) >> IPATCH_SAMPLE_STORE_FORMAT_SHIFT) \ & IPATCH_SAMPLE_FORMAT_MASK) /** * ipatch_sample_store_get_size: * * Macro for getting the sample size in frames of a sample store. No lock is required * since size can only be set prior to the store being actively used. * * Returns: Sample store size in frames. */ #define ipatch_sample_store_get_size(store) ((store)->size) /** * ipatch_sample_store_get_rate: * * Macro for getting the sample rate from a sample store. No lock is required * since rate can only be set prior to the store being actively used. * * Returns: Sample rate in HZ. */ #define ipatch_sample_store_get_rate(store) ((store)->rate) /** * ipatch_sample_store_get_size_bytes: * * Macro for getting the sample store data size in bytes. No lock is required * since format and size can only be set prior to the store being actively used. * * Returns: Sample store size in bytes. */ #define ipatch_sample_store_get_size_bytes(store) \ (ipatch_sample_format_size (ipatch_sample_store_get_format (store)) * (store)->size) /* we reserve flags for format and 3 for expansion */ #define IPATCH_SAMPLE_STORE_UNUSED_FLAG_SHIFT \ (IPATCH_ITEM_UNUSED_FLAG_SHIFT + IPATCH_SAMPLE_FORMAT_BITCOUNT + 3) GType ipatch_sample_store_get_type(void); IpatchSampleStore *ipatch_sample_store_first(IpatchIter *iter); IpatchSampleStore *ipatch_sample_store_next(IpatchIter *iter); #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreCache.c000066400000000000000000000246451400263525300233610ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSampleStoreCache * @short_description: Sample store object for cached samples in RAM * @see_also: #IpatchSampleData * @stability: Stable * * This sample store type is tightly integrated with #IpatchSampleData to provide * managed cached samples in RAM. */ #include #include #include #include #include "IpatchSampleStoreCache.h" #include "ipatch_priv.h" #include "i18n.h" /* properties */ enum { PROP_0, PROP_LOCATION }; /* Defined in IpatchSampleData.c */ extern void _ipatch_sample_data_cache_add_unused_size(int size); static void ipatch_sample_store_cache_sample_iface_init(IpatchSampleIface *iface); static void ipatch_sample_store_cache_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sample_store_cache_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sample_store_cache_finalize(GObject *object); static gboolean ipatch_sample_store_cache_sample_iface_open (IpatchSampleHandle *handle, GError **err); static void ipatch_sample_store_cache_sample_iface_close(IpatchSampleHandle *handle); static gboolean ipatch_sample_store_cache_sample_iface_read (IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err); static gboolean ipatch_sample_store_cache_sample_iface_write (IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err); G_DEFINE_TYPE_WITH_CODE(IpatchSampleStoreCache, ipatch_sample_store_cache, IPATCH_TYPE_SAMPLE_STORE, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_sample_store_cache_sample_iface_init)) static void ipatch_sample_store_cache_sample_iface_init(IpatchSampleIface *iface) { iface->open = ipatch_sample_store_cache_sample_iface_open; iface->close = ipatch_sample_store_cache_sample_iface_close; iface->read = ipatch_sample_store_cache_sample_iface_read; iface->write = ipatch_sample_store_cache_sample_iface_write; } static void ipatch_sample_store_cache_class_init(IpatchSampleStoreCacheClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->finalize = ipatch_sample_store_cache_finalize; obj_class->get_property = ipatch_sample_store_cache_get_property; item_class->item_set_property = ipatch_sample_store_cache_set_property; g_object_class_install_property(obj_class, PROP_LOCATION, g_param_spec_pointer("location", "Location", "Sample data pointer", G_PARAM_READWRITE)); } static void ipatch_sample_store_cache_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSampleStoreCache *store = IPATCH_SAMPLE_STORE_CACHE(object); switch(property_id) { case PROP_LOCATION: g_return_if_fail(store->location == NULL); /* Lock not needed, should be set only once before use */ store->location = g_value_get_pointer(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ipatch_sample_store_cache_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSampleStoreCache *store = IPATCH_SAMPLE_STORE_CACHE(object); switch(property_id) { case PROP_LOCATION: /* Lock not needed, should be set only once before use */ g_value_set_pointer(value, store->location); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ipatch_sample_store_cache_init(IpatchSampleStoreCache *store) { GTimeVal timeval; /* Initialize last open to current time */ g_get_current_time(&timeval); store->last_open = timeval.tv_sec; } static void ipatch_sample_store_cache_finalize(GObject *object) { IpatchSampleStoreCache *store = IPATCH_SAMPLE_STORE_CACHE(object); g_free(store->location); store->location = NULL; if(G_OBJECT_CLASS(ipatch_sample_store_cache_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_sample_store_cache_parent_class)->finalize(object); } } static gboolean ipatch_sample_store_cache_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchSampleStoreCache *store = IPATCH_SAMPLE_STORE_CACHE(handle->sample); guint bytes; g_return_val_if_fail(!handle->read_mode || store->location, FALSE); IPATCH_ITEM_WLOCK(store); store->last_open = 0; /* Reset time to 0 to indicate store is open */ if(store->open_count == 0) /* Recursive lock: store, sample_cache_vars */ _ipatch_sample_data_cache_add_unused_size (-(int)ipatch_sample_store_get_size_bytes((IpatchSampleStore *)store)); g_atomic_int_inc(&store->open_count); IPATCH_ITEM_WUNLOCK(store); /* Locking not needed, since new samples will be written with audio before * being used by multiple threads */ if(!store->location) { bytes = ipatch_sample_store_get_size_bytes((IpatchSampleStore *)store); store->location = g_malloc0(bytes); } /* Store frame size to data1 */ handle->data1 = GUINT_TO_POINTER(ipatch_sample_format_size(ipatch_sample_store_get_format(store))); return (TRUE); } static void ipatch_sample_store_cache_sample_iface_close(IpatchSampleHandle *handle) { IpatchSampleStoreCache *store = IPATCH_SAMPLE_STORE_CACHE(handle->sample); GTimeVal timeval; IPATCH_ITEM_WLOCK(store); /* Set last open time if there are no more open handles */ if(g_atomic_int_dec_and_test(&store->open_count)) { g_get_current_time(&timeval); store->last_open = timeval.tv_sec; /* Recursive lock: store, sample_cache_vars */ _ipatch_sample_data_cache_add_unused_size (ipatch_sample_store_get_size_bytes((IpatchSampleStore *)store)); } IPATCH_ITEM_WUNLOCK(store); } static gboolean ipatch_sample_store_cache_sample_iface_read(IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err) { IpatchSampleStoreCache *store = (IpatchSampleStoreCache *)(handle->sample); guint8 frame_size = GPOINTER_TO_UINT(handle->data1); /* No need to lock, sample data should not change after initial load */ memcpy(buf, &((gint8 *)(store->location))[offset * frame_size], frames * frame_size); return (TRUE); } static gboolean ipatch_sample_store_cache_sample_iface_write(IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err) { IpatchSampleStoreCache *store = (IpatchSampleStoreCache *)(handle->sample); guint8 frame_size = GPOINTER_TO_UINT(handle->data1); /* No need to lock, sample data written only once and before used by multiple threads */ memcpy(&((gint8 *)(store->location))[offset * frame_size], buf, frames * frame_size); return (TRUE); } /** * ipatch_sample_store_cache_new: * @location: Location of existing sample data or %NULL if the sample buffer * should be allocated (in which case the sample must be written to first). * * Creates a new cached RAM sample store. If @location is provided, its allocation * is taken over by the store. * * NOTE: This store type should not be used outside of the #IpatchSampleData * implementation, as it is tightly coupled with it. * * Returns: (type IpatchSampleStoreCache): New cached RAM sample store, cast * as a #IpatchSample for convenience. */ IpatchSample * ipatch_sample_store_cache_new(gpointer location) { return (IPATCH_SAMPLE(g_object_new(IPATCH_TYPE_SAMPLE_STORE_CACHE, "location", location, NULL))); } /** * ipatch_sample_store_cache_open: * @store: Sample cache store * * A dummy open function which can be used if the location pointer will be * accessed directly, rather than opening a #IpatchSampleHandle. Keeping a * cached sample store open will ensure it isn't destroyed. Call * ipatch_sample_store_cache_close() when done with it. */ void ipatch_sample_store_cache_open(IpatchSampleStoreCache *store) { int size; g_return_if_fail(IPATCH_IS_SAMPLE_STORE_CACHE(store)); size = ipatch_sample_store_get_size_bytes((IpatchSampleStore *)store); IPATCH_ITEM_WLOCK(store); store->last_open = 0; /* Reset time to 0 to indicate store is open */ if(store->open_count == 0) /* Recursive lock: store, sample_cache_vars */ { _ipatch_sample_data_cache_add_unused_size(-size); } g_atomic_int_inc(&store->open_count); IPATCH_ITEM_WUNLOCK(store); } /** * ipatch_sample_store_cache_close: * @store: Sample cache store * * A dummy close function which is called after a sample store cache is no * longer needed after opening it with ipatch_sample_store_cache_open(). */ void ipatch_sample_store_cache_close(IpatchSampleStoreCache *store) { GTimeVal timeval; int size; g_return_if_fail(IPATCH_IS_SAMPLE_STORE_CACHE(store)); size = ipatch_sample_store_get_size_bytes((IpatchSampleStore *)store); IPATCH_ITEM_WLOCK(store); /* Set last open time if there are no more open handles */ if(g_atomic_int_dec_and_test(&store->open_count)) { g_get_current_time(&timeval); store->last_open = timeval.tv_sec; /* Recursive lock: store, sample_cache_vars */ _ipatch_sample_data_cache_add_unused_size(size); } IPATCH_ITEM_WUNLOCK(store); } libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreCache.h000066400000000000000000000072131400263525300233560ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SAMPLE_STORE_CACHE_H__ #define __IPATCH_SAMPLE_STORE_CACHE_H__ #include #include #include /* forward type declarations */ typedef struct _IpatchSampleStoreCache IpatchSampleStoreCache; typedef struct _IpatchSampleStoreCacheClass IpatchSampleStoreCacheClass; #define IPATCH_TYPE_SAMPLE_STORE_CACHE (ipatch_sample_store_cache_get_type ()) #define IPATCH_SAMPLE_STORE_CACHE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SAMPLE_STORE_CACHE, \ IpatchSampleStoreCache)) #define IPATCH_SAMPLE_STORE_CACHE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SAMPLE_STORE_CACHE, \ IpatchSampleStoreCacheClass)) #define IPATCH_IS_SAMPLE_STORE_CACHE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SAMPLE_STORE_CACHE)) #define IPATCH_IS_SAMPLE_STORE_CACHE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SAMPLE_STORE_CACHE)) /* RAM sample store instance */ struct _IpatchSampleStoreCache { IpatchSampleStore parent_instance; gpointer location; /* Pointer to the sample data in memory */ guint32 channel_map; /* Channel map of cached sample in reference to native sample */ glong last_open; /* Unix time of last open or 0 if currently open */ int open_count; /* Current number of opens (atomic int) */ }; /* RAM sample store class */ struct _IpatchSampleStoreCacheClass { IpatchSampleStoreClass parent_class; }; /** * ipatch_sample_store_cache_get_location: * @store: Sample store to get sample data location from * * Macro to quickly fetch a cache sample store's data location pointer. * * Returns: Sample data pointer. */ #define ipatch_sample_store_cache_get_location(store) \ ((store)->location) /* No lock needed, set only once during creation */ /** * ipatch_sample_store_cache_get_channel_map: * @store: Sample store to get channel map from * * Macro to quickly fetch a cache sample store's channel map value. Cached * samples store a channel map in reference to the native sample of their * parent #IpatchSampleData. * * Returns: Channel map value. */ #define ipatch_sample_store_cache_get_channel_map(store) \ ((store)->channel_map) /* No lock needed, set only once during creation */ /* Used by #IpatchSampleData */ #define ipatch_sample_store_cache_get_open_count(store) \ g_atomic_int_get (&((store)->open_count)) /** * IPATCH_SAMPLE_STORE_CACHE_UNUSED_FLAG_SHIFT: (skip) */ /* we reserve 4 bits for future expansion */ #define IPATCH_SAMPLE_STORE_CACHE_UNUSED_FLAG_SHIFT \ (IPATCH_SAMPLE_STORE_UNUSED_FLAG_SHIFT + 4) GType ipatch_sample_store_cache_get_type(void); IpatchSample *ipatch_sample_store_cache_new(gpointer location); void ipatch_sample_store_cache_open(IpatchSampleStoreCache *store); void ipatch_sample_store_cache_close(IpatchSampleStoreCache *store); #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreFile.c000066400000000000000000000225341400263525300232300ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSampleStoreFile * @short_description: Sample store object type for audio in files on disk * @see_also: * @stability: Stable */ #include #include #include #include #include "IpatchSampleStoreFile.h" #include "ipatch_priv.h" #include "i18n.h" enum { PROP_0, PROP_TITLE, PROP_FILE, PROP_LOCATION }; static void ipatch_sample_store_file_sample_iface_init(IpatchSampleIface *iface); static void ipatch_sample_store_file_get_title(IpatchSampleStoreFile *store, GValue *value); static void ipatch_sample_store_file_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sample_store_file_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sample_store_file_finalize(GObject *object); static gboolean ipatch_sample_store_file_sample_iface_open (IpatchSampleHandle *handle, GError **err); static void ipatch_sample_store_file_sample_iface_close(IpatchSampleHandle *handle); static gboolean ipatch_sample_store_file_sample_iface_read (IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err); static gboolean ipatch_sample_store_file_sample_iface_write (IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err); G_DEFINE_TYPE_WITH_CODE(IpatchSampleStoreFile, ipatch_sample_store_file, IPATCH_TYPE_SAMPLE_STORE, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_sample_store_file_sample_iface_init)) static void ipatch_sample_store_file_sample_iface_init(IpatchSampleIface *iface) { iface->open = ipatch_sample_store_file_sample_iface_open; iface->close = ipatch_sample_store_file_sample_iface_close; iface->read = ipatch_sample_store_file_sample_iface_read; iface->write = ipatch_sample_store_file_sample_iface_write; } static void ipatch_sample_store_file_class_init(IpatchSampleStoreFileClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->finalize = ipatch_sample_store_file_finalize; obj_class->get_property = ipatch_sample_store_file_get_property; item_class->item_set_property = ipatch_sample_store_file_set_property; g_object_class_override_property(obj_class, PROP_TITLE, "title"); g_object_class_install_property(obj_class, PROP_FILE, g_param_spec_object("file", "File", "File object", IPATCH_TYPE_FILE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property(obj_class, PROP_LOCATION, g_param_spec_uint("location", "Location", "Sample data file location", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static void ipatch_sample_store_file_get_title(IpatchSampleStoreFile *store, GValue *value) { char *filename, *s, *basename = NULL; if(store->file) { filename = ipatch_file_get_name(store->file); /* ++ alloc filename */ if(filename) { basename = g_path_get_basename(filename); /* ++ alloc basename */ s = strrchr(basename, '.'); /* search for dot delimiter */ if(s && s > basename) { *s = '\0'; /* terminate string at dot */ } g_free(filename); /* -- free filename */ } } g_value_take_string(value, basename); /* !! caller takes over alloc */ } static void ipatch_sample_store_file_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSampleStoreFile *store = IPATCH_SAMPLE_STORE_FILE(object); /* No lock required since they should be set only once before use */ switch(property_id) { case PROP_FILE: g_return_if_fail(store->file == NULL); store->file = g_value_get_object(value); ipatch_file_ref_from_object(store->file, object); // ++ ref file from store /* IpatchItem notify for "title" property */ { GValue titleval = { 0 }; g_value_init(&titleval, G_TYPE_STRING); ipatch_sample_store_file_get_title(store, &titleval); ipatch_item_prop_notify((IpatchItem *)store, ipatch_item_pspec_title, &titleval, NULL); g_value_unset(&titleval); } break; case PROP_LOCATION: g_return_if_fail(store->location == 0); store->location = g_value_get_uint(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ipatch_sample_store_file_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSampleStoreFile *store = IPATCH_SAMPLE_STORE_FILE(object); /* No lock required since they should be set only once before use */ switch(property_id) { case PROP_TITLE: ipatch_sample_store_file_get_title(store, value); break; case PROP_FILE: g_value_set_object(value, store->file); break; case PROP_LOCATION: g_value_set_uint(value, store->location); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ipatch_sample_store_file_init(IpatchSampleStoreFile *store) { } static void ipatch_sample_store_file_finalize(GObject *object) { IpatchSampleStoreFile *store = IPATCH_SAMPLE_STORE_FILE(object); if(store->file) { ipatch_file_unref_from_object(store->file, object); // -- unref from object } if(G_OBJECT_CLASS(ipatch_sample_store_file_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_sample_store_file_parent_class)->finalize(object); } } static gboolean ipatch_sample_store_file_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchSampleStoreFile *store = IPATCH_SAMPLE_STORE_FILE(handle->sample); g_return_val_if_fail(store->file != NULL, FALSE); /* No lock needed - file object set only once */ handle->data1 = ipatch_file_open(store->file, NULL, handle->read_mode ? "r" : "w", err); if(!handle->data1) { return (FALSE); } handle->data2 = GUINT_TO_POINTER(ipatch_sample_format_size(ipatch_sample_store_get_format(store))); return (TRUE); } static void ipatch_sample_store_file_sample_iface_close(IpatchSampleHandle *handle) { if(handle->data1) { ipatch_file_close(handle->data1); handle->data1 = NULL; } } static gboolean ipatch_sample_store_file_sample_iface_read(IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err) { IpatchSampleStoreFile *store = (IpatchSampleStoreFile *)(handle->sample); IpatchFileHandle *fh = (IpatchFileHandle *)(handle->data1); guint8 frame_size = GPOINTER_TO_UINT(handle->data2); if(!ipatch_file_seek(fh, store->location + offset * frame_size, G_SEEK_SET, err)) { return (FALSE); } if(!ipatch_file_read(fh, buf, frames * frame_size, err)) { return (FALSE); } return (TRUE); } static gboolean ipatch_sample_store_file_sample_iface_write(IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err) { IpatchSampleStoreFile *store = (IpatchSampleStoreFile *)(handle->sample); IpatchFileHandle *fh = (IpatchFileHandle *)(handle->data1); guint8 frame_size = GPOINTER_TO_UINT(handle->data2); if(!ipatch_file_seek(fh, store->location + offset * frame_size, G_SEEK_SET, err)) { return (FALSE); } if(!ipatch_file_write(fh, buf, frames * frame_size, err)) { return (FALSE); } return (TRUE); } /** * ipatch_sample_store_file_new: * @file: File object to use for file sample store * @location: Location in file of audio data * * Creates a new file sample store. * * Returns: (type IpatchSampleStoreFile): New file sample store, cast * as a #IpatchSample for convenience. */ IpatchSample * ipatch_sample_store_file_new(IpatchFile *file, guint location) { return (IPATCH_SAMPLE(g_object_new(IPATCH_TYPE_SAMPLE_STORE_FILE, "file", file, "location", location, NULL))); } libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreFile.h000066400000000000000000000046651400263525300232420ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SAMPLE_STORE_FILE_H__ #define __IPATCH_SAMPLE_STORE_FILE_H__ #include #include #include #include /* forward type declarations */ typedef struct _IpatchSampleStoreFile IpatchSampleStoreFile; typedef struct _IpatchSampleStoreFileClass IpatchSampleStoreFileClass; #define IPATCH_TYPE_SAMPLE_STORE_FILE (ipatch_sample_store_file_get_type ()) #define IPATCH_SAMPLE_STORE_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SAMPLE_STORE_FILE, \ IpatchSampleStoreFile)) #define IPATCH_SAMPLE_STORE_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SAMPLE_STORE_FILE, \ IpatchSampleStoreFileClass)) #define IPATCH_IS_SAMPLE_STORE_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SAMPLE_STORE_FILE)) #define IPATCH_IS_SAMPLE_STORE_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SAMPLE_STORE_FILE)) /* File sample store instance */ struct _IpatchSampleStoreFile { IpatchSampleStore parent_instance; IpatchFile *file; /* File object */ guint location; /* Position in file of the sample data */ }; /* File sample store class */ struct _IpatchSampleStoreFileClass { IpatchSampleStoreClass parent_class; }; /** * IPATCH_SAMPLE_STORE_FILE_UNUSED_FLAG_SHIFT: (skip) */ /* reserve 1 private flag (IpatchSampleStoreFile.c) */ #define IPATCH_SAMPLE_STORE_FILE_UNUSED_FLAG_SHIFT \ (IPATCH_SAMPLE_STORE_UNUSED_FLAG_SHIFT + 1) GType ipatch_sample_store_file_get_type(void); IpatchSample *ipatch_sample_store_file_new(IpatchFile *file, guint location); #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreRam.c000066400000000000000000000214041400263525300230630ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSampleStoreRam * @short_description: Sample store object for audio data in RAM * @see_also: * @stability: Stable */ #include #include #include #include #include "IpatchSampleStoreRam.h" #include "ipatch_priv.h" #include "i18n.h" /* properties */ enum { PROP_0, PROP_LOCATION, PROP_FREE_DATA }; static void ipatch_sample_store_ram_sample_iface_init(IpatchSampleIface *iface); static void ipatch_sample_store_ram_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sample_store_ram_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sample_store_ram_finalize(GObject *object); static gboolean ipatch_sample_store_ram_sample_iface_open (IpatchSampleHandle *handle, GError **err); static gboolean ipatch_sample_store_ram_sample_iface_read (IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err); static gboolean ipatch_sample_store_ram_sample_iface_write (IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err); G_DEFINE_TYPE_WITH_CODE(IpatchSampleStoreRam, ipatch_sample_store_ram, IPATCH_TYPE_SAMPLE_STORE, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_sample_store_ram_sample_iface_init)) static void ipatch_sample_store_ram_sample_iface_init(IpatchSampleIface *iface) { iface->open = ipatch_sample_store_ram_sample_iface_open; iface->read = ipatch_sample_store_ram_sample_iface_read; iface->write = ipatch_sample_store_ram_sample_iface_write; } static void ipatch_sample_store_ram_class_init(IpatchSampleStoreRamClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->finalize = ipatch_sample_store_ram_finalize; obj_class->get_property = ipatch_sample_store_ram_get_property; item_class->item_set_property = ipatch_sample_store_ram_set_property; g_object_class_install_property(obj_class, PROP_LOCATION, g_param_spec_pointer("location", "Location", "Sample data pointer", G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_FREE_DATA, g_param_spec_boolean("free-data", "Free data", "Free data when object destroyed", FALSE, G_PARAM_READWRITE)); } static void ipatch_sample_store_ram_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSampleStoreRam *store = IPATCH_SAMPLE_STORE_RAM(object); switch(property_id) { case PROP_LOCATION: g_return_if_fail(store->location == NULL); /* Lock not needed, should be set only once before use */ store->location = g_value_get_pointer(value); break; case PROP_FREE_DATA: ipatch_item_set_flags(object, IPATCH_SAMPLE_STORE_RAM_ALLOCATED); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ipatch_sample_store_ram_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSampleStoreRam *store = IPATCH_SAMPLE_STORE_RAM(object); switch(property_id) { case PROP_LOCATION: /* Lock not needed, should be set only once before use */ g_value_set_pointer(value, store->location); break; case PROP_FREE_DATA: g_value_set_boolean(value, (ipatch_item_get_flags((IpatchItem *)object) & IPATCH_SAMPLE_STORE_RAM_ALLOCATED) != 0); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ipatch_sample_store_ram_init(IpatchSampleStoreRam *store) { } static void ipatch_sample_store_ram_finalize(GObject *object) { IpatchSampleStoreRam *store = IPATCH_SAMPLE_STORE_RAM(object); if(ipatch_item_get_flags(IPATCH_ITEM(store)) & IPATCH_SAMPLE_STORE_RAM_ALLOCATED) { g_free(store->location); store->location = NULL; } if(G_OBJECT_CLASS(ipatch_sample_store_ram_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_sample_store_ram_parent_class)->finalize(object); } } static gboolean ipatch_sample_store_ram_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchSampleStoreRam *store = IPATCH_SAMPLE_STORE_RAM(handle->sample); guint bytes; g_return_val_if_fail(!handle->read_mode || store->location, FALSE); /* Locking not needed, since new samples will be written with audio before * being used by multiple threads */ if(!store->location) { ipatch_item_set_flags(IPATCH_ITEM(store), IPATCH_SAMPLE_STORE_RAM_ALLOCATED); ipatch_sample_get_size(handle->sample, &bytes); store->location = g_malloc0(bytes); } /* Store frame size to data1 */ handle->data1 = GUINT_TO_POINTER(ipatch_sample_format_size(ipatch_sample_store_get_format(store))); return (TRUE); } static gboolean ipatch_sample_store_ram_sample_iface_read(IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err) { IpatchSampleStoreRam *store = (IpatchSampleStoreRam *)(handle->sample); guint8 frame_size = GPOINTER_TO_UINT(handle->data1); /* No need to lock, sample data should not change after initial load */ memcpy(buf, &((gint8 *)(store->location))[offset * frame_size], frames * frame_size); return (TRUE); } static gboolean ipatch_sample_store_ram_sample_iface_write(IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err) { IpatchSampleStoreRam *store = (IpatchSampleStoreRam *)(handle->sample); guint8 frame_size = GPOINTER_TO_UINT(handle->data1); /* No need to lock, sample data written only once and before used by multiple threads */ memcpy(&((gint8 *)(store->location))[offset * frame_size], buf, frames * frame_size); return (TRUE); } /** * ipatch_sample_store_ram_new: * @location: (nullable): Location of existing sample data or %NULL if the sample buffer * should be allocated (in which case the sample must be written to first). * @free_data: %TRUE if sample data at @location should be freed when object * is destroyed * * Creates a new RAM sample store. * * Returns: (type IpatchSampleStoreRam): New RAM sample store, * cast as a #IpatchSample for convenience. */ IpatchSample * ipatch_sample_store_ram_new(gpointer location, gboolean free_data) { return (IPATCH_SAMPLE(g_object_new(IPATCH_TYPE_SAMPLE_STORE_RAM, "location", location, "free-data", free_data, NULL))); } /** * ipatch_sample_store_ram_get_blank: * * Get blank mono RAM sample object. Return's a sample object * with 48 stereo 16 bit samples of silent audio. Only creates it on * the first call, subsequent calls return the same sample object. Therefore it * should not be modified. * * Returns: (transfer full): The blank sample object. Remember to unref it when not * using it anymore with g_object_unref(). */ IpatchSample * ipatch_sample_store_ram_get_blank(void) { static IpatchSample *blank_sample = NULL; gpointer dataptr; if(!blank_sample) { dataptr = g_malloc(48 * 2); blank_sample = ipatch_sample_store_ram_new(dataptr, TRUE); g_object_set(blank_sample, "sample-size", 48, "sample-format", IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_ENDIAN_HOST, "sample-rate", IPATCH_SAMPLE_RATE_DEFAULT, NULL); } else { g_object_ref(blank_sample); } return (blank_sample); } libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreRam.h000066400000000000000000000053711400263525300230750ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SAMPLE_STORE_RAM_H__ #define __IPATCH_SAMPLE_STORE_RAM_H__ #include #include #include /* forward type declarations */ typedef struct _IpatchSampleStoreRam IpatchSampleStoreRam; typedef struct _IpatchSampleStoreRamClass IpatchSampleStoreRamClass; #define IPATCH_TYPE_SAMPLE_STORE_RAM (ipatch_sample_store_ram_get_type ()) #define IPATCH_SAMPLE_STORE_RAM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SAMPLE_STORE_RAM, \ IpatchSampleStoreRam)) #define IPATCH_SAMPLE_STORE_RAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SAMPLE_STORE_RAM, \ IpatchSampleStoreRamClass)) #define IPATCH_IS_SAMPLE_STORE_RAM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SAMPLE_STORE_RAM)) #define IPATCH_IS_SAMPLE_STORE_RAM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SAMPLE_STORE_RAM)) /* RAM sample store instance */ struct _IpatchSampleStoreRam { IpatchSampleStore parent_instance; gpointer location; /* Pointer to the sample data in memory */ }; /* RAM sample store class */ struct _IpatchSampleStoreRamClass { IpatchSampleStoreClass parent_class; }; /** * IpatchSampleStoreRamFlags: * @IPATCH_SAMPLE_STORE_RAM_ALLOCATED: Indicates if sample data was allocated * and therefore should be freed when finalized. * * Flags crammed into #IpatchItem flags field. */ typedef enum { IPATCH_SAMPLE_STORE_RAM_ALLOCATED = 1 << IPATCH_SAMPLE_STORE_UNUSED_FLAG_SHIFT } IpatchSampleStoreRamFlags; /** * IPATCH_SAMPLE_STORE_RAM_UNUSED_FLAG_SHIFT: (skip) */ /* we reserve 1 bits for defined flags above and 3 bits for future expansion */ #define IPATCH_SAMPLE_STORE_RAM_UNUSED_FLAG_SHIFT \ (IPATCH_SAMPLE_STORE_UNUSED_FLAG_SHIFT + 4) GType ipatch_sample_store_ram_get_type(void); IpatchSample *ipatch_sample_store_ram_new(gpointer location, gboolean free_data); IpatchSample *ipatch_sample_store_ram_get_blank(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreRom.c000066400000000000000000000111001400263525300230710ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSampleStoreRom * @short_description: Sample storage object for audio in ROM of a sound card * @see_also: * @stability: Stable */ #include #include #include "IpatchSampleStoreRom.h" #include "ipatch_priv.h" enum { PROP_0, PROP_LOCATION, /* location property (used by all types) */ }; static void ipatch_sample_store_rom_sample_iface_init(IpatchSampleIface *iface); static void ipatch_sample_store_rom_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sample_store_rom_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gboolean ipatch_sample_store_rom_sample_iface_open (IpatchSampleHandle *handle, GError **err); G_DEFINE_TYPE_WITH_CODE(IpatchSampleStoreRom, ipatch_sample_store_rom, IPATCH_TYPE_SAMPLE_STORE, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_sample_store_rom_sample_iface_init)) static void ipatch_sample_store_rom_sample_iface_init(IpatchSampleIface *iface) { iface->open = ipatch_sample_store_rom_sample_iface_open; } static void ipatch_sample_store_rom_class_init(IpatchSampleStoreRomClass *klass) { GObjectClass *gobj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); gobj_class->get_property = ipatch_sample_store_rom_get_property; item_class->item_set_property = ipatch_sample_store_rom_set_property; g_object_class_install_property(gobj_class, PROP_LOCATION, g_param_spec_uint("location", "Location", "Sample data ROM location", 0, G_MAXUINT, 0, G_PARAM_READWRITE)); } static void ipatch_sample_store_rom_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSampleStoreRom *store = IPATCH_SAMPLE_STORE_ROM(object); switch(property_id) { case PROP_LOCATION: g_return_if_fail(store->location == 0); /* Only set once, no lock required */ store->location = g_value_get_uint(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ipatch_sample_store_rom_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSampleStoreRom *store = IPATCH_SAMPLE_STORE_ROM(object); switch(property_id) { case PROP_LOCATION: /* No need to lock, only set once before use */ g_value_set_uint(value, store->location); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ipatch_sample_store_rom_init(IpatchSampleStoreRom *store) { } static gboolean ipatch_sample_store_rom_sample_iface_open(IpatchSampleHandle *handle, GError **err) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_PROGRAM, "ROM sample stores cannot be opened"); return (FALSE); } /** * ipatch_sample_store_rom_new: * @location: Location in ROM * * Creates a new rom sample store. No data can actually be read or written * from this store type. Its used only to keep track of ROM locations in older * SoundFont files. * * Returns: (type IpatchSampleStoreRom): New rom sample store, cast * as an #IpatchSample for convenience. */ IpatchSample * ipatch_sample_store_rom_new(guint location) { return (IPATCH_SAMPLE(g_object_new(IPATCH_TYPE_SAMPLE_STORE_ROM, "location", location, NULL))); } libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreRom.h000066400000000000000000000040201400263525300231010ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SAMPLE_STORE_ROM_H__ #define __IPATCH_SAMPLE_STORE_ROM_H__ #include #include #include typedef struct _IpatchSampleStoreRom IpatchSampleStoreRom; typedef struct _IpatchSampleStoreRomClass IpatchSampleStoreRomClass; #define IPATCH_TYPE_SAMPLE_STORE_ROM (ipatch_sample_store_rom_get_type ()) #define IPATCH_SAMPLE_STORE_ROM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SAMPLE_STORE_ROM, \ IpatchSampleStoreRom)) #define IPATCH_SAMPLE_STORE_ROM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SAMPLE_STORE_ROM, \ IpatchSampleStoreRomClass)) #define IPATCH_IS_SAMPLE_STORE_ROM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SAMPLE_STORE_ROM)) #define IPATCH_IS_SAMPLE_STORE_ROM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SAMPLE_STORE_ROM)) /* ROM sample store instance */ struct _IpatchSampleStoreRom { IpatchSampleStore parent_instance; guint location; }; /* ROM sample store class */ struct _IpatchSampleStoreRomClass { IpatchSampleStoreClass parent_class; }; GType ipatch_sample_store_rom_get_type(void); IpatchSample *ipatch_sample_store_rom_new(guint location); #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreSndFile.c000066400000000000000000000741261400263525300237010ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSampleStoreSndFile * @short_description: Sample store object type which uses libsndfile to access * audio in sound files * @see_also: * @stability: Stable */ #include #include #include #include "IpatchSampleStoreSndFile.h" #include "IpatchSampleStore.h" #include "IpatchSndFile.h" #include "ipatch_priv.h" #include "builtin_enums.h" #include "i18n.h" /* * Notes: * * In read mode the audio file must be identified before being opened for * reading. This is necessary in order to assign the correct #IpatchSample:format * to the sample store, based on the file's format. * * PCM formats are read and written using sf_read/write_raw to allow for more * flexibility and to remove the need to do extra conversions (in the case of * 8 bit, 24 bit or non CPU endian formats). * * Non-PCM formats are read/written as 16 bit CPU endian order data. */ enum { PROP_0, PROP_TITLE, PROP_LOOP_TYPE, PROP_LOOP_START, PROP_LOOP_END, PROP_ROOT_NOTE, PROP_FINE_TUNE, PROP_FILE_NAME, PROP_FILE_FORMAT, PROP_SUB_FORMAT, PROP_ENDIAN }; static void ipatch_sample_store_snd_file_sample_iface_init(IpatchSampleIface *iface); static void ipatch_sample_store_snd_file_get_title(IpatchSampleStoreSndFile *store, GValue *value); static void ipatch_sample_store_snd_file_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sample_store_snd_file_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sample_store_snd_file_finalize(GObject *object); static gboolean ipatch_sample_store_snd_file_sample_iface_open (IpatchSampleHandle *handle, GError **err); static gboolean verify_read_format(IpatchSampleStoreSndFile *store); static gboolean verify_write_format(IpatchSampleStoreSndFile *store); static void ipatch_sample_store_snd_file_sample_iface_close(IpatchSampleHandle *handle); static gboolean ipatch_sample_store_snd_file_sample_iface_read (IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err); static gboolean ipatch_sample_store_snd_file_sample_iface_write (IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err); static int libsndfile_read_format_convert(int sndfile_format, int channels, gboolean *raw); static int libsndfile_write_format_convert(int sndfile_format, int channels); /* Supported loop types */ static int ipatch_sample_store_snd_file_loop_types[] = { IPATCH_SAMPLE_LOOP_NONE, IPATCH_SAMPLE_LOOP_STANDARD, IPATCH_SAMPLE_LOOP_PINGPONG, IPATCH_SAMPLE_LOOP_TYPE_TERM /* terminator */ }; G_DEFINE_TYPE_WITH_CODE(IpatchSampleStoreSndFile, ipatch_sample_store_snd_file, IPATCH_TYPE_SAMPLE_STORE, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_sample_store_snd_file_sample_iface_init)) static void ipatch_sample_store_snd_file_sample_iface_init(IpatchSampleIface *iface) { iface->open = ipatch_sample_store_snd_file_sample_iface_open; iface->close = ipatch_sample_store_snd_file_sample_iface_close; iface->read = ipatch_sample_store_snd_file_sample_iface_read; iface->write = ipatch_sample_store_snd_file_sample_iface_write; iface->loop_types = ipatch_sample_store_snd_file_loop_types; } static void ipatch_sample_store_snd_file_class_init(IpatchSampleStoreSndFileClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); GType file_format, file_sub_format; obj_class->finalize = ipatch_sample_store_snd_file_finalize; obj_class->get_property = ipatch_sample_store_snd_file_get_property; item_class->item_set_property = ipatch_sample_store_snd_file_set_property; g_object_class_override_property(obj_class, PROP_TITLE, "title"); ipatch_sample_install_property(obj_class, PROP_LOOP_TYPE, "loop-type"); ipatch_sample_install_property(obj_class, PROP_LOOP_START, "loop-start"); ipatch_sample_install_property(obj_class, PROP_LOOP_END, "loop-end"); ipatch_sample_install_property(obj_class, PROP_ROOT_NOTE, "root-note"); ipatch_sample_install_property(obj_class, PROP_FINE_TUNE, "fine-tune"); g_object_class_install_property(obj_class, PROP_FILE_NAME, g_param_spec_string("file-name", "File name", "File name", NULL, G_PARAM_READWRITE)); file_format = ipatch_snd_file_format_get_type(); g_object_class_install_property(obj_class, PROP_FILE_FORMAT, g_param_spec_enum("file-format", "File format", "File format", file_format, IPATCH_SND_FILE_DEFAULT_FORMAT, G_PARAM_READWRITE)); file_sub_format = ipatch_snd_file_sub_format_get_type(); g_object_class_install_property(obj_class, PROP_SUB_FORMAT, g_param_spec_enum("sub-format", "File sub format", "File sub audio format", file_sub_format, IPATCH_SND_FILE_DEFAULT_SUB_FORMAT, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_ENDIAN, g_param_spec_enum("endian", "Endian byte order", "Endian byte order of file", IPATCH_TYPE_SND_FILE_ENDIAN, IPATCH_SND_FILE_DEFAULT_ENDIAN, G_PARAM_READWRITE)); } static void ipatch_sample_store_snd_file_get_title(IpatchSampleStoreSndFile *store, GValue *value) { char *filename, *s, *basename = NULL; g_object_get(store, "file-name", &filename, NULL); /* ++ alloc filename */ if(filename) { basename = g_path_get_basename(filename); /* ++ alloc basename */ s = strrchr(basename, '.'); /* search for dot delimiter */ if(s && s > basename) { *s = '\0'; /* terminate string at dot */ } g_free(filename); /* -- free filename */ } g_value_take_string(value, basename); /* !! caller takes over alloc */ } static void ipatch_sample_store_snd_file_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSampleStoreSndFile *store = IPATCH_SAMPLE_STORE_SND_FILE(object); /* No locking needed - all values should be set prior to multi-thread use */ switch(property_id) { case PROP_LOOP_TYPE: store->loop_type = g_value_get_enum(value); break; case PROP_LOOP_START: store->loop_start = g_value_get_uint(value); break; case PROP_LOOP_END: store->loop_end = g_value_get_uint(value); break; case PROP_ROOT_NOTE: store->root_note = g_value_get_int(value); break; case PROP_FINE_TUNE: store->fine_tune = g_value_get_int(value); break; case PROP_FILE_NAME: g_free(store->filename); store->filename = g_value_dup_string(value); /* IpatchItem notify for "title" property */ { GValue titleval = { 0 }; g_value_init(&titleval, G_TYPE_STRING); ipatch_sample_store_snd_file_get_title(store, &titleval); ipatch_item_prop_notify((IpatchItem *)store, ipatch_item_pspec_title, &titleval, NULL); g_value_unset(&titleval); } break; case PROP_FILE_FORMAT: store->file_format = g_value_get_enum(value); break; case PROP_SUB_FORMAT: store->sub_format = g_value_get_enum(value); break; case PROP_ENDIAN: store->endian = g_value_get_enum(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ipatch_sample_store_snd_file_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSampleStoreSndFile *store = IPATCH_SAMPLE_STORE_SND_FILE(object); /* No locking needed - all values should be set prior to multi-thread use */ switch(property_id) { case PROP_TITLE: ipatch_sample_store_snd_file_get_title(store, value); break; case PROP_LOOP_TYPE: g_value_set_enum(value, store->loop_type); break; case PROP_LOOP_START: g_value_set_uint(value, store->loop_start); break; case PROP_LOOP_END: g_value_set_uint(value, store->loop_end); break; case PROP_ROOT_NOTE: g_value_set_int(value, store->root_note); break; case PROP_FINE_TUNE: g_value_set_int(value, store->fine_tune); break; case PROP_FILE_NAME: g_value_set_string(value, store->filename); break; case PROP_FILE_FORMAT: g_value_set_enum(value, store->file_format); break; case PROP_SUB_FORMAT: g_value_set_enum(value, store->sub_format); break; case PROP_ENDIAN: g_value_set_enum(value, store->endian); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ipatch_sample_store_snd_file_init(IpatchSampleStoreSndFile *sample) { sample->file_format = IPATCH_SND_FILE_DEFAULT_FORMAT; sample->sub_format = IPATCH_SND_FILE_DEFAULT_SUB_FORMAT; sample->endian = IPATCH_SND_FILE_DEFAULT_ENDIAN; sample->loop_type = IPATCH_SAMPLE_LOOP_NONE; sample->root_note = IPATCH_SAMPLE_ROOT_NOTE_DEFAULT; } static void ipatch_sample_store_snd_file_finalize(GObject *object) { IpatchSampleStoreSndFile *sample = IPATCH_SAMPLE_STORE_SND_FILE(object); g_free(sample->filename); if(G_OBJECT_CLASS(ipatch_sample_store_snd_file_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_sample_store_snd_file_parent_class)->finalize(object); } } static gboolean ipatch_sample_store_snd_file_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchSampleStoreSndFile *store = IPATCH_SAMPLE_STORE_SND_FILE(handle->sample); SF_INFO info; int format; g_return_val_if_fail(store->filename != NULL, FALSE); format = ipatch_sample_store_get_format(store); if(handle->read_mode) { if(!store->identified) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID, _("Sample file '%s' has not yet been identified for reading"), store->filename); return (FALSE); } if(!verify_read_format(store)) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID, _("Invalid read format values for sample file '%s'"), store->filename); return (FALSE); } if(store->raw) { handle->data2 = sf_read_raw; /* Just read raw data (conversions handled internally) */ handle->data3 = GUINT_TO_POINTER(ipatch_sample_format_size(format)); } else { switch(IPATCH_SAMPLE_FORMAT_GET_WIDTH(format)) { case IPATCH_SAMPLE_16BIT: handle->data2 = sf_readf_short; break; case IPATCH_SAMPLE_32BIT: handle->data2 = sf_readf_int; break; case IPATCH_SAMPLE_FLOAT: handle->data2 = sf_readf_float; break; case IPATCH_SAMPLE_DOUBLE: handle->data2 = sf_readf_double; break; default: g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID, _("Inconsistent state for sample file '%s'"), store->filename); return (FALSE); } handle->data3 = GUINT_TO_POINTER(1); /* Multiplier is 1, for number of frames */ } } else /* Write mode */ { if(!verify_write_format(store)) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID, _("Invalid write format values for sample file '%s'"), store->filename); return (FALSE); } switch(IPATCH_SAMPLE_FORMAT_GET_WIDTH(format)) { case IPATCH_SAMPLE_8BIT: handle->data2 = sf_write_raw; /* Just write raw data (conversions handled internally) */ handle->data3 = GUINT_TO_POINTER(ipatch_sample_format_size(format)); break; case IPATCH_SAMPLE_16BIT: handle->data2 = sf_writef_short; handle->data3 = GUINT_TO_POINTER(1); break; case IPATCH_SAMPLE_32BIT: handle->data2 = sf_writef_int; handle->data3 = GUINT_TO_POINTER(1); break; case IPATCH_SAMPLE_FLOAT: handle->data2 = sf_writef_float; handle->data3 = GUINT_TO_POINTER(1); break; case IPATCH_SAMPLE_DOUBLE: handle->data2 = sf_writef_double; handle->data3 = GUINT_TO_POINTER(1); break; } } /* Write mode? - Fill in format structure */ if(!handle->read_mode) { info.samplerate = ((IpatchSampleStore *)store)->rate; info.channels = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format); info.format = store->file_format | store->sub_format | store->endian; if(!sf_format_check(&info)) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID, _("Invalid libsndfile format for file '%s' (format: 0x%08X, chan: %d, rate: %d)"), store->filename, info.format, info.channels, info.samplerate); return (FALSE); } } else { info.format = 0; } /* Open the file using libsndfile */ handle->data1 = sf_open(store->filename, handle->read_mode ? SFM_READ : SFM_WRITE, &info); if(!handle->data1) { if(handle->read_mode) g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, _("Error opening file '%s' for reading"), store->filename); else g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, _("Error opening file '%s' for writing"), store->filename); return (FALSE); } /* Store instrument info if write mode and not default values */ if(!handle->read_mode && (store->loop_type != IPATCH_SAMPLE_LOOP_NONE || store->root_note != IPATCH_SAMPLE_ROOT_NOTE_DEFAULT || store->fine_tune != 0)) { SF_INSTRUMENT instinfo; memset(&instinfo, 0, sizeof(instinfo)); instinfo.basenote = store->root_note; instinfo.detune = store->fine_tune; instinfo.velocity_lo = 0; instinfo.velocity_hi = 127; instinfo.key_lo = 0; instinfo.key_hi = 127; if(store->loop_type != IPATCH_SAMPLE_LOOP_NONE) { instinfo.loop_count = 1; switch(store->loop_type) { case IPATCH_SAMPLE_LOOP_PINGPONG: instinfo.loops[0].mode = SF_LOOP_ALTERNATING; break; case IPATCH_SAMPLE_LOOP_STANDARD: case IPATCH_SAMPLE_LOOP_RELEASE: default: instinfo.loops[0].mode = SF_LOOP_FORWARD; break; } instinfo.loops[0].start = store->loop_start; instinfo.loops[0].end = store->loop_end; } sf_command(handle->data1, SFC_SET_INSTRUMENT, &instinfo, sizeof(instinfo)); } /* Used as current offset (in frames) into file to optimize out seek calls and prevent * failure when attempting unnecessary seeks in formats which don't support it. */ handle->data4 = GUINT_TO_POINTER(0); return (TRUE); } /* Verify that libsndfile and IpatchSample format parameters are consistent for reading */ static gboolean verify_read_format(IpatchSampleStoreSndFile *store) { int format, conv_format; gboolean raw; format = ipatch_sample_store_get_format(store); conv_format = libsndfile_read_format_convert(store->file_format | store->sub_format | store->endian, IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format), &raw); return (format == conv_format && raw == store->raw); } /* Verify that libsndfile and IpatchSample format parameters are consistent for writing */ static gboolean verify_write_format(IpatchSampleStoreSndFile *store) { int format, conv_format; format = ipatch_sample_store_get_format(store); conv_format = libsndfile_write_format_convert(store->file_format | store->sub_format | store->endian, IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format)); return (format == conv_format); } static void ipatch_sample_store_snd_file_sample_iface_close(IpatchSampleHandle *handle) { if(handle->data1) { sf_close(handle->data1); handle->data1 = NULL; } } static gboolean ipatch_sample_store_snd_file_sample_iface_read(IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err) { IpatchSampleStoreSndFile *sample = (IpatchSampleStoreSndFile *)(handle->sample); SNDFILE *sfhandle = (SNDFILE *)(handle->data1); sf_count_t (*readfunc)(SNDFILE * sndfile, gpointer buf, sf_count_t count) = handle->data2; guint count = frames * GPOINTER_TO_UINT(handle->data3); /* data3 is frames multiplier */ guint filepos = GPOINTER_TO_UINT(handle->data4); /* data4 is current offset */ sf_count_t read_count; if(offset != filepos && sf_seek(sfhandle, offset, SEEK_SET) == -1) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, _("libsndfile error seeking file '%s': %s"), sample->filename, sf_strerror(sfhandle)); return (FALSE); } read_count = readfunc(sfhandle, buf, count); if(read_count != count) { if(sf_error(sfhandle) == SF_ERR_NO_ERROR) g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNEXPECTED_EOF, _("Unexpected end of file in '%s'"), sample->filename); else g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, _("libsndfile error reading file '%s': %s"), sample->filename, sf_strerror(sfhandle)); return (FALSE); } filepos += frames; handle->data4 = GUINT_TO_POINTER(filepos); return (TRUE); } static gboolean ipatch_sample_store_snd_file_sample_iface_write(IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err) { IpatchSampleStoreSndFile *sample = (IpatchSampleStoreSndFile *)(handle->sample); SNDFILE *sfhandle = (SNDFILE *)(handle->data1); sf_count_t (*writefunc)(SNDFILE * sndfile, gconstpointer buf, sf_count_t count) = handle->data2; guint count = frames * GPOINTER_TO_UINT(handle->data3); /* data3 is frames multiplier */ if(sf_seek(sfhandle, offset, SEEK_SET) == -1) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, _("libsndfile error seeking file '%s': %s"), sample->filename, sf_strerror(sfhandle)); return (FALSE); } if(writefunc(sfhandle, buf, count) != count) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, _("libsndfile error writing file '%s': %s"), sample->filename, sf_strerror(sfhandle)); return (FALSE); } return (TRUE); } /** * ipatch_sample_store_snd_file_new: * @filename: File name to assign to the new libsndfile sample store * * Creates a new libsndfile sample store. ipatch_sample_store_snd_file_init_read() * or ipatch_sample_store_snd_file_init_write() must be called, depending on * audio file mode, prior to opening the sample. * * Returns: (type IpatchSampleStoreSndFile): New libsndfile sample store, cast * as a #IpatchSample for convenience. */ IpatchSample * ipatch_sample_store_snd_file_new(const char *filename) { return (IPATCH_SAMPLE(g_object_new(IPATCH_TYPE_SAMPLE_STORE_SND_FILE, "file-name", filename, NULL))); } /** * ipatch_sample_store_snd_file_init_read: * @store: libsndfile sample store * * Initialize a libsndfile sample store for reading. Should be called prior to * opening the sample store and after the filename has been assigned. Fills in * the #IpatchSampleStoreSndFile:file-format, #IpatchSampleStoreSndFile:sub-format, * #IpatchSampleStoreSndFile:endian, #IpatchSample:sample-rate and * #IpatchSample:sample-size information properties. In addition the * #IpatchSample:sample-format property is set to a value for optimal loading of the * audio data (least amount of conversion necessary to yield uncompressed PCM * audio), which will be the audio format of the sample store. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean ipatch_sample_store_snd_file_init_read(IpatchSampleStoreSndFile *store) { SF_INSTRUMENT instinfo; SNDFILE *sfhandle; SF_INFO info; int format; memset(&info, 0, sizeof(SF_INFO)); /* Open the file for reading using libsndfile */ sfhandle = sf_open(store->filename, SFM_READ, &info); if(!sfhandle) { return (FALSE); } format = libsndfile_read_format_convert(info.format, info.channels, &store->raw); g_object_set(store, "file-format", (int)(info.format & SF_FORMAT_TYPEMASK), "sub-format", (int)(info.format & SF_FORMAT_SUBMASK), "endian", (int)(info.format & SF_FORMAT_ENDMASK), "sample-rate", (int)(info.samplerate), "sample-size", (guint)(info.frames), "sample-format", (int)(format), NULL); if(sf_command(sfhandle, SFC_GET_INSTRUMENT, &instinfo, sizeof(instinfo))) { int loop_type; unsigned int loop_start, loop_end; if(instinfo.loop_count > 0) { switch(instinfo.loops[0].mode) { case SF_LOOP_NONE: loop_type = IPATCH_SAMPLE_LOOP_NONE; break; case SF_LOOP_ALTERNATING: loop_type = IPATCH_SAMPLE_LOOP_PINGPONG; break; case SF_LOOP_FORWARD: case SF_LOOP_BACKWARD: /* FIXME - Is there really a loop backward mode? */ default: loop_type = IPATCH_SAMPLE_LOOP_STANDARD; break; } loop_start = instinfo.loops[0].start; loop_end = instinfo.loops[0].end; } else { loop_type = IPATCH_SAMPLE_LOOP_NONE; loop_start = loop_end = 0; } g_object_set(store, "root-note", (int)(instinfo.basenote), "fine-tune", (int)(instinfo.detune), "loop-type", (int)(loop_type), "loop-start", (guint)(loop_start), "loop-end", (guint)(loop_end), NULL); } sf_close(sfhandle); store->identified = TRUE; return (TRUE); } /** * ipatch_sample_store_snd_file_init_write: * @store: libsndfile sample store * @file_format: libsndfile file format type (GEnum "IpatchSndFileFormat") * @sub_format: libsndfile audio format type (GEnum "IpatchSndFileSubFormat") * @endian: libsndfile endian selection (#IpatchSampleStoreSndFileEndian) * @channels: Number of channels (1-8, 1=mono, 2=stereo, etc) * @samplerate: Audio sample rate * * Initialize a libsndfile sample store for writing. Should be called prior to * opening the sample store. The #IpatchSampleStoreSndFile:file-format, * #IpatchSampleStoreSndFile:sub-format, #IpatchSampleStoreSndFile:endian * and #IpatchSample:sample-rate properties will be assigned the provided values. * In addition the #IpatchSample:sample-format property is set to a value for optimal * writing of the audio data (including the @channels value), which will be the * audio format of the sample store. * * Returns: %TRUE if format variables are valid, %FALSE otherwise */ gboolean ipatch_sample_store_snd_file_init_write(IpatchSampleStoreSndFile *store, int file_format, int sub_format, int endian, int channels, int samplerate) { SF_INFO info; int sample_format; g_return_val_if_fail(IPATCH_IS_SAMPLE_STORE_SND_FILE(store), FALSE); g_return_val_if_fail(channels >= 1 && channels <= 8, FALSE); info.samplerate = samplerate; info.channels = channels; info.format = file_format | sub_format | endian; if(!sf_format_check(&info)) { return (FALSE); } sample_format = libsndfile_write_format_convert(info.format, channels); g_object_set(store, "file-format", file_format, "sub-format", sub_format, "endian", endian, "sample-rate", samplerate, "sample-format", sample_format, NULL); return (TRUE); } /** * libsndfile_read_format_convert: * @sndfile_format: libsndfile file/audio format * @channels: Number of channels (1-8, 1=mono, 2=stereo, etc) * @raw: Location to store boolean value indicating if data should be read raw, * or %NULL to ignore * * "convert" a libsndfile format to a compatible libinstpatch format for reading. * * Returns: A compatible libinstpatch sample format */ static int libsndfile_read_format_convert(int sndfile_format, int channels, gboolean *raw) { gboolean rawval; int file_format; int format; file_format = sndfile_format & SF_FORMAT_TYPEMASK; /* Some formats return PCM sub formats even if they aren't actually RAW PCM, * such as FLAC. This is bummer dude, since we must upconvert 24 bit to 32 bit, * 8 bit to 16 bit, etc or revert to nasty conversions requiring a second * buffer. We trust that certain formats will have RAW PCM that we can just * read ourselves and do whatever we like with it. An unfortunate limitation of * libsndfile. */ if(file_format == SF_FORMAT_WAV || file_format == SF_FORMAT_AIFF) { rawval = TRUE; } else { rawval = FALSE; } switch(sndfile_format & SF_FORMAT_SUBMASK) { case SF_FORMAT_PCM_S8: format = rawval ? IPATCH_SAMPLE_8BIT : IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_ENDIAN_HOST; break; case SF_FORMAT_PCM_16: format = rawval ? IPATCH_SAMPLE_16BIT : IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_ENDIAN_HOST; break; case SF_FORMAT_PCM_24: format = rawval ? IPATCH_SAMPLE_REAL24BIT : IPATCH_SAMPLE_32BIT | IPATCH_SAMPLE_ENDIAN_HOST; break; case SF_FORMAT_PCM_32: format = rawval ? IPATCH_SAMPLE_32BIT : IPATCH_SAMPLE_32BIT | IPATCH_SAMPLE_ENDIAN_HOST; break; case SF_FORMAT_PCM_U8: format = rawval ? IPATCH_SAMPLE_8BIT | IPATCH_SAMPLE_UNSIGNED : IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_ENDIAN_HOST; break; case SF_FORMAT_FLOAT: format = IPATCH_SAMPLE_FLOAT | IPATCH_SAMPLE_ENDIAN_HOST; rawval = FALSE; break; case SF_FORMAT_DOUBLE: format = IPATCH_SAMPLE_DOUBLE | IPATCH_SAMPLE_ENDIAN_HOST; rawval = FALSE; break; default: /* Just read non PCM formats as 16 bit host endian */ format = IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_ENDIAN_HOST; rawval = FALSE; break; } if(rawval) { switch(sndfile_format & SF_FORMAT_ENDMASK) { case SF_ENDIAN_LITTLE: format |= IPATCH_SAMPLE_LENDIAN; break; case SF_ENDIAN_BIG: format |= IPATCH_SAMPLE_BENDIAN; break; default: format |= IPATCH_SAMPLE_ENDIAN_HOST; break; } } format |= ((channels - 1) & 0x07) << IPATCH_SAMPLE_CHANNEL_SHIFT; if(raw) { *raw = rawval; } return (format); } /** * libsndfile_write_format_convert: * @sndfile_format: libsndfile file/audio format * @channels: Number of channels (1-8, 1=mono, 2=stereo, etc) * * "convert" a libsndfile format to a compatible libinstpatch format for writing. * * Returns: A compatible libinstpatch sample format */ static int libsndfile_write_format_convert(int sndfile_format, int channels) { int format; switch(sndfile_format & SF_FORMAT_SUBMASK) { case SF_FORMAT_PCM_S8: format = IPATCH_SAMPLE_8BIT; break; case SF_FORMAT_PCM_16: format = IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_ENDIAN_HOST; break; case SF_FORMAT_PCM_24: /* No libsndfile function for writing 24 bit */ case SF_FORMAT_PCM_32: format = IPATCH_SAMPLE_32BIT | IPATCH_SAMPLE_ENDIAN_HOST; break; case SF_FORMAT_PCM_U8: format = IPATCH_SAMPLE_8BIT | IPATCH_SAMPLE_UNSIGNED; break; case SF_FORMAT_FLOAT: format = IPATCH_SAMPLE_FLOAT | IPATCH_SAMPLE_ENDIAN_HOST; break; case SF_FORMAT_DOUBLE: format = IPATCH_SAMPLE_DOUBLE | IPATCH_SAMPLE_ENDIAN_HOST; break; default: /* Just write non PCM formats as 16 bit host endian */ format = IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_ENDIAN_HOST; break; } format |= ((channels - 1) & 0x07) << IPATCH_SAMPLE_CHANNEL_SHIFT; return (format); } libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreSndFile.h000066400000000000000000000065111400263525300236770ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SAMPLE_STORE_SND_FILE_H__ #define __IPATCH_SAMPLE_STORE_SND_FILE_H__ #include #include #include #include /* forward type declarations */ typedef struct _IpatchSampleStoreSndFile IpatchSampleStoreSndFile; typedef struct _IpatchSampleStoreSndFileClass IpatchSampleStoreSndFileClass; #define IPATCH_TYPE_SAMPLE_STORE_SND_FILE (ipatch_sample_store_snd_file_get_type ()) #define IPATCH_SAMPLE_STORE_SND_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SAMPLE_STORE_SND_FILE, \ IpatchSampleStoreSndFile)) #define IPATCH_SAMPLE_STORE_SND_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SAMPLE_STORE_SND_FILE, \ IpatchSampleStoreSndFileClass)) #define IPATCH_IS_SAMPLE_STORE_SND_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SAMPLE_STORE_SND_FILE)) #define IPATCH_IS_SAMPLE_STORE_SND_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SAMPLE_STORE_SND_FILE)) /* libsndfile sample instance */ struct _IpatchSampleStoreSndFile { IpatchSampleStore parent_instance; char *filename; /* File name where sample data is */ gboolean identified; /* TRUE if file has been identified (for reading) */ gboolean raw; /* TRUE if audio data is read raw from libsndfile */ int file_format; /* File format enum (enum is dynamic) */ int sub_format; /* File sub format enum (enum is dynamic) */ int endian; /* File endian byte order enum */ guint loop_start; /* loop start */ guint loop_end; /* loop end */ guint8 loop_type; /* loop type */ guint8 root_note; /* root note */ gint8 fine_tune; /* fine tune */ }; /* libsndfile sample class */ struct _IpatchSampleStoreSndFileClass { IpatchSampleStoreClass parent_class; }; /** * IPATCH_SAMPLE_STORE_SND_FILE_UNUSED_FLAG_SHIFT: (skip) */ /* we reserve 3 flags for expansion */ #define IPATCH_SAMPLE_STORE_SND_FILE_UNUSED_FLAG_SHIFT \ (IPATCH_SAMPLE_STORE_UNUSED_FLAG_SHIFT + 3) GType ipatch_sample_store_snd_file_get_type(void); IpatchSample *ipatch_sample_store_snd_file_new(const char *filename); gboolean ipatch_sample_store_snd_file_init_read(IpatchSampleStoreSndFile *store); gboolean ipatch_sample_store_snd_file_init_write(IpatchSampleStoreSndFile *store, int file_format, int sub_format, int endian, int channels, int samplerate); #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreSplit24.c000066400000000000000000000225231400263525300236100ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSampleStoreSplit24 * @short_description: Sample storage object for 24 bit audio in 16 and 8 bit segments * @see_also: * @stability: Stable * * SoundFont 2.04 adds support for 24 bit audio. This is done in a semi * backwards compatible fashion where the most significant 16 bits is stored * separately from the remaining 8 bit segments. This storage object handles * this transparently. */ #include #include #include #include "IpatchSampleStoreSplit24.h" #include "ipatch_priv.h" #include "i18n.h" enum { PROP_0, PROP_LOCATION_LSBYTES }; /* Size of allocated copy buffer for each open sample handle */ #define READBUF_SIZE 16384 static void ipatch_sample_store_split24_sample_iface_init(IpatchSampleIface *iface); static void ipatch_sample_store_split24_class_init(IpatchSampleStoreSplit24Class *klass); static void ipatch_sample_store_split24_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sample_store_split24_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gboolean ipatch_sample_store_split24_sample_iface_open (IpatchSampleHandle *handle, GError **err); static void ipatch_sample_store_split24_sample_iface_close(IpatchSampleHandle *handle); static gboolean ipatch_sample_store_split24_sample_iface_read (IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err); G_DEFINE_TYPE_WITH_CODE(IpatchSampleStoreSplit24, ipatch_sample_store_split24, IPATCH_TYPE_SAMPLE_STORE_FILE, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_sample_store_split24_sample_iface_init)) static void ipatch_sample_store_split24_sample_iface_init(IpatchSampleIface *iface) { iface->open = ipatch_sample_store_split24_sample_iface_open; iface->close = ipatch_sample_store_split24_sample_iface_close; iface->read = ipatch_sample_store_split24_sample_iface_read; iface->write = NULL; } static void ipatch_sample_store_split24_class_init(IpatchSampleStoreSplit24Class *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->get_property = ipatch_sample_store_split24_get_property; item_class->item_set_property = ipatch_sample_store_split24_set_property; g_object_class_install_property(obj_class, PROP_LOCATION_LSBYTES, g_param_spec_uint("location-lsbytes", "Location LS-Bytes", "LS byte sample data file position", 0, G_MAXUINT, 0, G_PARAM_READWRITE)); } static void ipatch_sample_store_split24_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSampleStoreSplit24 *split24 = IPATCH_SAMPLE_STORE_SPLIT24(object); switch(property_id) { case PROP_LOCATION_LSBYTES: g_return_if_fail(split24->loc_lsbytes == 0); /* Only set once before use, no lock required */ split24->loc_lsbytes = g_value_get_uint(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ipatch_sample_store_split24_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSampleStoreSplit24 *split24 = IPATCH_SAMPLE_STORE_SPLIT24(object); switch(property_id) { case PROP_LOCATION_LSBYTES: /* Only set once before use, no lock required */ g_value_set_uint(value, split24->loc_lsbytes); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ipatch_sample_store_split24_init(IpatchSampleStoreSplit24 *store) { } static gboolean ipatch_sample_store_split24_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchSampleStoreSplit24 *split24_store = (IpatchSampleStoreSplit24 *)(handle->sample); IpatchSampleStoreFile *file_store = (IpatchSampleStoreFile *)split24_store; int fmt; g_return_val_if_fail(file_store->file != NULL, FALSE); g_return_val_if_fail(file_store->location != 0, FALSE); g_return_val_if_fail(split24_store->loc_lsbytes != 0, FALSE); fmt = ipatch_sample_store_get_format(split24_store); fmt &= ~IPATCH_SAMPLE_ENDIAN_MASK; /* we can do either endian */ g_return_val_if_fail(fmt == IPATCH_SAMPLE_24BIT, FALSE); /* No lock needed - file object set only once */ handle->data1 = ipatch_file_open(file_store->file, NULL, handle->read_mode ? "r" : "w", err); if(!handle->data1) { return (FALSE); } handle->data2 = g_malloc(READBUF_SIZE); return (TRUE); } static void ipatch_sample_store_split24_sample_iface_close(IpatchSampleHandle *handle) { if(handle->data1) { ipatch_file_close(handle->data1); g_free(handle->data2); handle->data1 = NULL; handle->data2 = NULL; } } static gboolean ipatch_sample_store_split24_sample_iface_read(IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err) { IpatchSampleStoreSplit24 *split24_store = (IpatchSampleStoreSplit24 *)(handle->sample); IpatchSampleStoreFile *file_store = (IpatchSampleStoreFile *)split24_store; guint samplepos, thissize, curofs; gboolean lilendian; IpatchFileHandle *fhandle = (IpatchFileHandle *)(handle->data1); guint8 *readbuf = (guint8 *)(handle->data2); guint8 *i8p; guint i; lilendian = (ipatch_sample_store_get_format(split24_store) & IPATCH_SAMPLE_ENDIAN_MASK) == IPATCH_SAMPLE_LENDIAN; samplepos = 0; curofs = offset; thissize = READBUF_SIZE / 2; // 16 bit samples /* copy 16 bit sample data */ while(samplepos < frames) { if(frames - samplepos < thissize) { thissize = frames - samplepos; } if(!ipatch_file_seek(fhandle, file_store->location + curofs * 2, G_SEEK_SET, err)) { return (FALSE); } if(!ipatch_file_read(fhandle, readbuf, thissize * 2, err)) { return (FALSE); } i8p = (guint8 *)buf + samplepos * 4; if(lilendian) { for(i = 0; i < thissize; i++) { i8p[i * 4 + 1] = readbuf[i * 2]; i8p[i * 4 + 2] = readbuf[i * 2 + 1]; i8p[i * 4 + 3] = 0; } } else { for(i = 0; i < thissize; i++) { i8p[i * 4 + 2] = readbuf[i * 2]; i8p[i * 4 + 1] = readbuf[i * 2 + 1]; i8p[i * 4 + 0] = 0; } } samplepos += thissize; curofs += thissize; } samplepos = 0; curofs = offset; thissize = READBUF_SIZE; /* copy upper byte of 24 bit samples */ while(samplepos < frames) { if(frames - samplepos < thissize) { thissize = frames - samplepos; } if(!ipatch_file_seek(fhandle, split24_store->loc_lsbytes + curofs, G_SEEK_SET, err)) { return (FALSE); } if(!ipatch_file_read(fhandle, readbuf, thissize, err)) { return (FALSE); } i8p = (guint8 *)buf + samplepos * 4; if(lilendian) { for(i = 0; i < thissize; i++) { i8p[i * 4] = readbuf[i]; } } else { for(i = 0; i < thissize; i++) { i8p[i * 4 + 3] = readbuf[i]; } } samplepos += thissize; curofs += thissize; } return (TRUE); } /** * ipatch_sample_store_split24_new: * @file: File object * @loc_16bit: Location of 16 bit audio data * @loc_lsbytes: Location of 24 bit LS bytes * * Creates a new split 24 bit sample store (lower byte of 24 bit * samples is stored in a separate block). New SoundFont 2.04 uses this method. * * Returns: (type IpatchSampleStoreSplit24): New split 24 sample store */ IpatchSample * ipatch_sample_store_split24_new(IpatchFile *file, guint loc_16bit, guint loc_lsbytes) { return (IPATCH_SAMPLE(g_object_new(IPATCH_TYPE_SAMPLE_STORE_SPLIT24, "file", file, "location", loc_16bit, "location-lsbytes", loc_lsbytes, NULL))); } libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreSplit24.h000066400000000000000000000044501400263525300236140ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SAMPLE_STORE_SPLIT24_H__ #define __IPATCH_SAMPLE_STORE_SPLIT24_H__ #include #include #include #include /* forward type declarations */ typedef struct _IpatchSampleStoreSplit24 IpatchSampleStoreSplit24; typedef struct _IpatchSampleStoreSplit24Class IpatchSampleStoreSplit24Class; #define IPATCH_TYPE_SAMPLE_STORE_SPLIT24 (ipatch_sample_store_split24_get_type ()) #define IPATCH_SAMPLE_STORE_SPLIT24(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SAMPLE_STORE_SPLIT24, \ IpatchSampleStoreSplit24)) #define IPATCH_SAMPLE_STORE_SPLIT24_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SAMPLE_STORE_SPLIT24, \ IpatchSampleStoreSplit24Class)) #define IPATCH_IS_SAMPLE_STORE_SPLIT24(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SAMPLE_STORE_SPLIT24)) #define IPATCH_IS_SAMPLE_STORE_SPLIT24_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SAMPLE_STORE_SPLIT24)) /* File sample store instance */ struct _IpatchSampleStoreSplit24 { IpatchSampleStoreFile parent_instance; guint loc_lsbytes; /* pos of the least significant bytes of 24 bit audio */ }; /* File sample store class */ struct _IpatchSampleStoreSplit24Class { IpatchSampleStoreFileClass parent_class; }; GType ipatch_sample_store_split24_get_type(void); IpatchSample *ipatch_sample_store_split24_new(IpatchFile *file, guint loc_16bit, guint loc_lsbytes); #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreSwap.c000066400000000000000000000771261400263525300232720ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSampleStoreSwap * @short_description: Sample storage object for audio in memory or temporary * swap file * @see_also: * @stability: Stable * * Swap sample stores are used for data which does not have a safe external * source, for example if a sample was originally loaded from an external * audio file or an instrument file that was closed. * * Swap sample stores are stored in RAM up to the total size set by * ipatch_sample_store_swap_set_max_memory(). Additional sample stores * are written to the swap file, whose file name is set by * ipatch_sample_store_set_file_name() with a fallback to a temporary file * name if not set. * * Currently there is a global lock on read or write accesses of sample stores * in the swap file. This is contrary to most other sample store types. * * When a sample store in the swap file is no longer used, it is added to a * recover list, which new sample stores may utilize. This cuts down on unused * space in the swap file (ipatch_sample_store_swap_get_unused_size()), which * can be compacted with ipatch_sample_store_swap_compact(). */ #include #include #include #include #include #include #include "IpatchSampleStoreSwap.h" #include "ipatch_priv.h" #include "compat.h" #include "i18n.h" #include "config.h" #define MAX_RAM_SWAP (32*1024*1024) // Default maximum RAM memory swap /* Indicates if a swap sample store has been allocated space */ #define SAMPLE_ALLOCATED (1 << IPATCH_SAMPLE_STORE_UNUSED_FLAG_SHIFT) /* Keeps track of areas in the swap file which are no longer used and can be * re-used by new samples */ typedef struct { guint size; guint location; } SwapRecover; static void ipatch_sample_store_swap_recover_free(SwapRecover *recover); static gint ipatch_sample_store_swap_recover_size_sort_func(gconstpointer a, gconstpointer b); static void ipatch_sample_store_swap_sample_iface_init(IpatchSampleIface *iface); static gboolean ipatch_sample_store_swap_sample_iface_open(IpatchSampleHandle *handle, GError **err); static void ipatch_sample_store_swap_open_file(void); static gboolean ipatch_sample_store_swap_sample_iface_read(IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err); static gboolean ipatch_sample_store_swap_sample_iface_write(IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err); static void ipatch_sample_store_swap_finalize(GObject *gobject); G_LOCK_DEFINE_STATIC(swap); static int swap_fd = -1; static char *swap_file_name = NULL; static guint swap_position = 0; // Current position in swap file, for new sample data static volatile gint swap_unused_size = 0; // Amount of wasted space (unused samples) static volatile gint swap_ram_used = 0; // Amount of RAM memory used for swap static volatile gint swap_ram_max = MAX_RAM_SWAP; // Maximum amount of RAM swap storage static GSList *swap_list = NULL; // List of #IpatchSampleStoreSwap objects stored on disk // Both recover lists share the same SwapRecover structure static GSList *swap_recover_list = NULL; // List of SwapRecover structures sorted by size field (from larger to smaller) static GSList *swap_recover_loc_list = NULL; // List of SwapRecover structures sorted by location field (from low to high) G_DEFINE_TYPE_WITH_CODE(IpatchSampleStoreSwap, ipatch_sample_store_swap, IPATCH_TYPE_SAMPLE_STORE, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_sample_store_swap_sample_iface_init)) /* ----- Initialization/deinitialization of lists ---------------------------*/ /* Initialize lists */ void _ipatch_sample_store_swap_recover_init(void) { swap_fd = -1; swap_file_name = NULL; swap_position = 0; // Current position in swap file, for new sample data swap_unused_size = 0; // Amount of wasted space (unused samples) swap_ram_used = 0; // Amount of RAM memory used for swap swap_ram_max = MAX_RAM_SWAP; // Maximum amount of RAM swap storage swap_list = NULL; swap_recover_list = NULL; swap_recover_loc_list = NULL; } /* Free lists */ void _ipatch_sample_store_swap_recover_deinit(void) { g_slist_free_full (swap_recover_list, (GDestroyNotify)ipatch_sample_store_swap_recover_free); g_slist_free (swap_recover_loc_list); g_free(swap_file_name); } /* ----- IpatchSampleStoreSwap object functions ----------------------------*/ static SwapRecover * ipatch_sample_store_swap_recover_new(void) { return (g_slice_new0(SwapRecover)); } static void ipatch_sample_store_swap_recover_free(SwapRecover *recover) { g_slice_free(SwapRecover, recover); } /* Add a recover segment to the recover pool, merging segments if possible. * NOTE: swap must be locked by caller. Allocation of recover structure is taken over. */ static void ipatch_sample_store_swap_recover_add(SwapRecover *recover) { GSList *p, *next, *prev; SwapRecover *compare, *compare2; guint end; end = recover->location + recover->size; // See if this segment can be joined with other ones for(p = swap_recover_loc_list, prev = NULL; p; prev = p, p = p->next) { compare = (SwapRecover *)(p->data); // Can this segment be merged? if(compare->location == end || compare->location + compare->size == recover->location) { compare->size += recover->size; if(compare->location != end) // If recover segment comes after compare, see if the next segment can be merged too { next = p->next; if(next) { compare2 = (SwapRecover *)(next->data); if(compare->location + compare->size == compare2->location) { compare->size += compare2->size; p->next = next->next; g_slist_free_1(next); // Free the 2nd segment and list node ipatch_sample_store_swap_recover_free(compare2); swap_recover_list = g_slist_remove(swap_recover_list, compare2); // Remove from recover size list also } } } else { compare->location = recover->location; // Recover segment comes before, no more mergable segments } // Remove merged segment from recover size list and re-insert sorted by its new size swap_recover_list = g_slist_remove(swap_recover_list, compare); swap_recover_list = g_slist_insert_sorted(swap_recover_list, compare, ipatch_sample_store_swap_recover_size_sort_func); ipatch_sample_store_swap_recover_free(recover); // -- Recover segment was merged, free it recover = NULL; break; } if(compare->location > recover->location) { break; } } if(recover) // Recover segment didn't get merged? - insert it into both lists { p = g_slist_append(NULL, recover); p->next = prev ? prev->next : swap_recover_loc_list; if(prev) { prev->next = p; } else { swap_recover_loc_list = p; } swap_recover_list = g_slist_insert_sorted(swap_recover_list, recover, // !! takes over allocation of recover structure ipatch_sample_store_swap_recover_size_sort_func); } } // Function to sort recover segments in largest to smallest order static gint ipatch_sample_store_swap_recover_size_sort_func(gconstpointer a, gconstpointer b) { const SwapRecover *recover_a = a, *recover_b = b; return (recover_b->size - recover_a->size); } static void ipatch_sample_store_swap_sample_iface_init(IpatchSampleIface *iface) { iface->open = ipatch_sample_store_swap_sample_iface_open; iface->read = ipatch_sample_store_swap_sample_iface_read; iface->write = ipatch_sample_store_swap_sample_iface_write; } static void ipatch_sample_store_swap_class_init(IpatchSampleStoreSwapClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); obj_class->finalize = ipatch_sample_store_swap_finalize; } static gboolean ipatch_sample_store_swap_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchSampleStoreSwap *store = IPATCH_SAMPLE_STORE_SWAP(handle->sample); gboolean already_allocated_or_write_mode; guint sample_size; gint new_ram_used; guint flags; SwapRecover *recover; GSList *p, *prev, *prevprev; ipatch_sample_get_size(IPATCH_SAMPLE(store), &sample_size); flags = ipatch_item_get_flags(IPATCH_ITEM(store)); g_return_val_if_fail(sample_size > 0, FALSE); already_allocated_or_write_mode = (flags & SAMPLE_ALLOCATED) || !handle->read_mode; g_return_val_if_fail(already_allocated_or_write_mode, FALSE); // !! No lock needed on sample store since sample data is set once only, prior to multi-thread usage // Has sample been allocated? if(!(flags & SAMPLE_ALLOCATED)) { new_ram_used = g_atomic_int_exchange_and_add(&swap_ram_used, sample_size); new_ram_used += sample_size; // Value returned is the amount before the add // Check if allocating sample in RAM would exceed max allowed if(new_ram_used > g_atomic_int_get(&swap_ram_max)) { // RAM swap is maxed out - correct swap_ram_used g_atomic_int_add(&swap_ram_used, -(gint)sample_size); if(swap_fd == -1) /* Swap file not yet created? */ { ipatch_sample_store_swap_open_file(); } if(swap_fd != -1) { G_LOCK(swap); // ++ lock swap_list // See if there are any recover segments that can be used (find closest size, they are sorted largest to smallest) for(p = swap_recover_list, prev = NULL, prevprev = NULL; p; prevprev = prev, prev = p, p = p->next) { recover = (SwapRecover *)(p->data); if(recover->size < sample_size) { break; } } if(prev) { recover = (SwapRecover *)(prev->data); store->location = recover->location; recover->size -= sample_size; recover->location += sample_size; g_atomic_int_add(&swap_unused_size, -(gint)sample_size); // Remove the node from the size recover list if(prevprev) { prevprev->next = prev->next; } else { swap_recover_list = prev->next; } g_slist_free_1(prev); // Delete list node swap_recover_loc_list = g_slist_remove(swap_recover_loc_list, recover); // Remove from location list if(recover->size > 0) { ipatch_sample_store_swap_recover_add(recover); // Re-add segment to recover lists } else { ipatch_sample_store_swap_recover_free(recover); // Free empty recover structure } } else // No adequate recover segment found, reserve new area { store->location = swap_position; swap_position += sample_size; } swap_list = g_slist_prepend(swap_list, store); // Append disk swap store to swap_list G_UNLOCK(swap); // -- unlock swap_list } else { store->ram_location = g_malloc(sample_size); // Failed to open swap file - allocate sample store in memory } } else { store->ram_location = g_malloc(sample_size); // Allocate sample store in memory } ipatch_item_set_flags(IPATCH_ITEM(store), SAMPLE_ALLOCATED); } handle->data1 = GUINT_TO_POINTER(ipatch_sample_format_size(ipatch_sample_store_get_format(store))); return (TRUE); } /* Opens swap file (either assigned file name or temporary file) */ /* The application could set the swap file name calling following functions: 1)ipatch_set_sample_store_swap_file_name(swap_filename) 2)However, when multiple applications are calling libinstpatch it is best for each application to call ipatch_set_application_name(). In this case, if the application do not call ipatch_set_sample_store_swap_file_name(), a swap file name is build using application name as prefix of swap file name. For example calling ipatch_set_sample_store_swap_file_name("swami") leads to a default swap file "swami-swap_XXXXXX" 3)If ipatch_set_sample_store_swap_file_name() or ipatch_set_application_name() are not called, the default file name is "libInstPatch-swap_XXXXXX" In case 2 and 3, the swap file directory is the one used for temporary files returned by g_get_tmp_dir(). */ static void ipatch_sample_store_swap_open_file(void) { char *template = NULL, *s; G_LOCK(swap); // ++ lock swap if(swap_file_name) // Use existing name if it was assigned { #ifdef G_OS_WIN32 /* On Windows, calling g_open leads to memory violation access on next call to lseek. */ swap_fd = _open(swap_file_name, O_RDWR | O_CREAT, _S_IREAD | _S_IWRITE); #else swap_fd = g_open (swap_file_name, O_RDWR | O_CREAT, 0600); #endif if(swap_fd != -1) { G_UNLOCK(swap); // -- unlock swap return; } g_warning(_("Failed to open sample swap file '%s': %s"), swap_file_name, g_strerror(errno)); // Failed to open swap file, free assigned file name and fall back to temporary file g_free(swap_file_name); swap_file_name = NULL; } // Use application name, if set, as prefix of swap file if(ipatch_application_name) { template = g_strdup(ipatch_application_name); // ++ allocate template application name string s = strchr(template, ' '); // Search for space version separator in application name if(s) { *s = '\0'; } if(strlen(template) > 0) { s = g_strconcat(template, "-swap_XXXXXX", NULL); // ++ allocate full template string g_free(template); // -- free old template string template = s; } else { g_free(template); // -- free template string template = NULL; } } /* Open swap file name in temporary directory */ #ifdef G_OS_WIN32 /* On Windows, calling g_open leads to memory violation access on next call to lseek. */ /* ++ alloc swap_file_name */ swap_file_name = g_build_filename(g_get_tmp_dir(), template ? template : "libInstPatch-swap_XXXXXX", NULL); swap_fd = _open(swap_file_name, O_RDWR | O_CREAT, _S_IREAD | _S_IWRITE); g_free (template); // -- free template string (if set) if (swap_fd == -1) { g_critical(_("Failed to create temp sample store swap file: %s"), swap_file_name); } #else { GError *local_err = NULL; swap_fd = g_file_open_tmp(template ? template : "libInstPatch-swap_XXXXXX", &swap_file_name, &local_err); g_free(template); // -- free template string (if set) if (swap_fd == -1) { g_critical(_("Failed to create temp sample store swap file: %s"), ipatch_gerror_message(local_err)); g_clear_error (&local_err); } } #endif G_UNLOCK(swap); // -- unlock swap } static gboolean ipatch_sample_store_swap_sample_iface_read(IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err) { unsigned int retval; IpatchSampleStoreSwap *store = (IpatchSampleStoreSwap *)(handle->sample); guint8 frame_size = GPOINTER_TO_UINT(handle->data1); if(store->ram_location) { memcpy(buf, ((guint8 *)store->ram_location) + offset * frame_size, frames * frame_size); return (TRUE); } G_LOCK(swap); // ++ lock swap if(IPATCH_FD_LSEEK(swap_fd, store->location + offset * frame_size, SEEK_SET) == -1) { G_UNLOCK(swap); // -- unlock swap g_set_error(err, G_FILE_ERROR, g_file_error_from_errno(errno), _("Error seeking in sample store swap file: %s"), g_strerror(errno)); return (FALSE); } retval = IPATCH_FD_READ(swap_fd, buf, frames * frame_size); if(retval == -1) g_set_error(err, G_FILE_ERROR, g_file_error_from_errno(errno), _("Error reading from sample store swap file: %s"), g_strerror(errno)); else if(retval < frames * frame_size) g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, _("Short read from sample store swap file, expected %d but got %" G_GSSIZE_FORMAT), frames * frame_size, retval); G_UNLOCK(swap); // -- unlock swap return (retval == frames * frame_size); } static gboolean ipatch_sample_store_swap_sample_iface_write(IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err) { unsigned int retval; IpatchSampleStoreSwap *store = (IpatchSampleStoreSwap *)(handle->sample); guint8 frame_size = GPOINTER_TO_UINT(handle->data1); if(store->ram_location) { memcpy(((guint8 *)store->ram_location) + offset * frame_size, buf, frames * frame_size); return (TRUE); } G_LOCK(swap); // ++ lock swap if(IPATCH_FD_LSEEK(swap_fd, store->location + offset * frame_size, SEEK_SET) == -1) { G_UNLOCK(swap); // -- unlock swap g_set_error(err, G_FILE_ERROR, g_file_error_from_errno(errno), _("Error seeking in sample store swap file: %s"), g_strerror(errno)); return (FALSE); } retval = IPATCH_FD_WRITE(swap_fd, buf, frames * frame_size); if(retval == -1) g_set_error(err, G_FILE_ERROR, g_file_error_from_errno(errno), _("Error writing to sample store swap file: %s"), g_strerror(errno)); else if(retval < frames * frame_size) g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, _("Short write to sample store swap file, expected %d but got %" G_GSSIZE_FORMAT), frames * frame_size, retval); G_UNLOCK(swap); // -- unlock swap return (retval == frames * frame_size); } static void ipatch_sample_store_swap_init(IpatchSampleStoreSwap *store) { } /* finalization for a swap sample store, keeps track of unused data size */ static void ipatch_sample_store_swap_finalize(GObject *gobject) { IpatchSampleStoreSwap *store = IPATCH_SAMPLE_STORE_SWAP(gobject); SwapRecover *recover; guint size; size = ipatch_sample_store_get_size_bytes((IpatchSampleStore *)store); if(store->ram_location) // Allocated in RAM? { g_atomic_int_add(&swap_ram_used, -(gint)size); // Subtract size from RAM usage g_free(store->ram_location); // -- free allocated RAM } else { recover = ipatch_sample_store_swap_recover_new(); // ++ alloc recover structure recover->size = size; recover->location = store->location; G_LOCK(swap); // ++ lock swap_list swap_list = g_slist_remove(swap_list, store); // Remove disk swap store from swap_list ipatch_sample_store_swap_recover_add(recover); // Add the recover segment G_UNLOCK(swap); // -- unlock swap_list g_atomic_int_add(&swap_unused_size, size); // Store allocated in disk swap file, add to unused size } if(G_OBJECT_CLASS(ipatch_sample_store_swap_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_sample_store_swap_parent_class)->finalize(gobject); } } /** * ipatch_set_sample_store_swap_file_name: * @filename: File name to use for sample swap disk file * * Set name of sample swap storage file on disk. Can only be assigned once and * should be done prior to any #IpatchSampleStoreSwap objects being created. * * Since: 1.1.0 */ void ipatch_set_sample_store_swap_file_name(const char *filename) { g_return_if_fail(filename != NULL); g_return_if_fail(swap_file_name == NULL); swap_file_name = g_strdup(filename); } /** * ipatch_get_sample_store_swap_file_name: * * Get name of sample swap storage file on disk. * * Returns: Newly allocated sample store swap file name or %NULL * * Since: 1.1.0 */ char * ipatch_get_sample_store_swap_file_name(void) { return (g_strdup(swap_file_name)); // !! allocate for caller } /** * ipatch_sample_store_swap_new: * * Creates a new disk swap sample store. * * Returns: (type IpatchSampleStoreSwap): New disk swap sample store, cast * as an #IpatchSample for convenience. */ IpatchSample * ipatch_sample_store_swap_new(void) { return (IPATCH_SAMPLE(g_object_new(IPATCH_TYPE_SAMPLE_STORE_SWAP, NULL))); } /** * ipatch_sample_store_swap_close: * * Close the swap sample store temporary file and delete it. Should only be called prior to * exiting application when no more sample store accesses will occur. * * Since: 1.1.0 */ void ipatch_sample_store_swap_close(void) { G_LOCK(swap); // ++ lock swap if(swap_fd != -1) { IPATCH_FD_CLOSE(swap_fd); swap_fd = -1; // Just blindly delete the swap file if(swap_file_name) { g_unlink(swap_file_name); } } G_UNLOCK(swap); // -- unlock swap } /** * ipatch_get_sample_store_swap_unused_size: * * Get amount of unused space in the swap file. * * Returns: Amount of unused data in bytes * * Since: 1.1.0 */ int ipatch_get_sample_store_swap_unused_size(void) { return (g_atomic_int_get(&swap_unused_size)); } /** * ipatch_set_sample_store_swap_max_memory: * @size: Maximum amount of RAM to use for swap sample stores (-1 for unlimited) * * Set maximum RAM memory size to use for samples in swap. Using RAM increases * performance, at the expense of memory use. Once max RAM usage is exceeded * samples will be allocated in sample swap file on disk. * * Since: 1.1.0 */ void ipatch_set_sample_store_swap_max_memory(int size) { g_atomic_int_set(&swap_ram_max, size); } /** * ipatch_get_sample_store_swap_max_memory: * * Get maximum RAM memory size to use for samples in swap. * * Returns: Max sample store swap RAM memory size. * * Since: 1.1.0 */ int ipatch_get_sample_store_swap_max_memory(void) { return (g_atomic_int_get(&swap_ram_max)); } /** * ipatch_sample_store_swap_compact: * @err: Location to store error information or %NULL to ignore * * Compact the sample store swap file by re-writing it to a new file * and creating new sample stores to replace the old ones. This should be * done when the unused size (ipatch_get_sample_store_swap_unused_size()) * exceeds a certain amount. This occurs when sample stores in the swap file * are no longer used, leaving gaps of unused data. If there is no unused data * then nothing is done. * NOTE: Swap file will be locked at multi thread sensitive phases of this operation * which may cause simultaneous sample operations on swap samples to be delayed. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set) * * Since: 1.1.0 */ gboolean ipatch_compact_sample_store_swap(GError **err) { IpatchSampleStoreSwap *store; guint8 *buf; char *newname; int newfd; GArray *position_array; GSList *p; guint size, ofs; int retval, this_size; guint i; g_return_val_if_fail(!err || !*err, FALSE); // No unused data? - Return success if(ipatch_get_sample_store_swap_unused_size() == 0) { return (TRUE); } // Create new swap file to copy existing disk samples to newname = g_strconcat(swap_file_name, "_new", NULL); // ++ alloc new file name (same as existing one + _new) #ifdef G_OS_WIN32 /* On Windows, calling g_open leads to memory violation access on next call to lseek. */ newfd = _open (newname, O_RDWR | O_CREAT, _S_IREAD | _S_IWRITE); #else newfd = g_open (newname, O_RDWR | O_CREAT, 0600); #endif if(newfd == -1) { g_set_error(err, G_FILE_ERROR, g_file_error_from_errno(errno), _("Failed to open new swap file '%s': %s"), newname, g_strerror(errno)); g_free(newname); // -- free newname return (FALSE); } buf = g_malloc(IPATCH_SAMPLE_COPY_BUFFER_SIZE); // ++ alloc copy buffer position_array = g_array_new(FALSE, FALSE, sizeof(guint)); // ++ alloc array G_LOCK(swap); // ++ lock swap swap_position = 0; for(p = swap_list; p; p = p->next) { store = (IpatchSampleStoreSwap *)(p->data); ipatch_sample_get_size(IPATCH_SAMPLE(store), &size); ofs = 0; g_array_append_val(position_array, swap_position); this_size = IPATCH_SAMPLE_COPY_BUFFER_SIZE; while(ofs < size) { if(size - ofs < IPATCH_SAMPLE_COPY_BUFFER_SIZE) { this_size = size - ofs; } swap_position += this_size; if(IPATCH_FD_LSEEK(swap_fd, store->location + ofs, SEEK_SET) == -1) { g_set_error(err, G_FILE_ERROR, g_file_error_from_errno(errno), _("Error seeking in sample store swap file: %s"), g_strerror(errno)); goto error; } ofs += this_size; retval = IPATCH_FD_READ(swap_fd, buf, this_size); if(retval == -1) { g_set_error(err, G_FILE_ERROR, g_file_error_from_errno(errno), _("Error reading from sample store swap file: %s"), g_strerror(errno)); goto error; } else if(retval < this_size) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, _("Short read from sample store swap file, expected %d but got %d"), this_size, retval); goto error; } retval = IPATCH_FD_WRITE(newfd, buf, this_size); if(retval == -1) { g_set_error(err, G_FILE_ERROR, g_file_error_from_errno(errno), _("Error writing to new sample store swap file: %s"), g_strerror(errno)); goto error; } else if(retval < this_size) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, _("Short write to new sample store swap file, expected %d but got %d"), this_size, retval); goto error; } } } // Free the recover lists g_slist_free_full(swap_recover_list, (GDestroyNotify)ipatch_sample_store_swap_recover_free); g_slist_free(swap_recover_loc_list); swap_recover_list = NULL; swap_recover_loc_list = NULL; g_atomic_int_set(&swap_unused_size, 0); // Set unused size back to 0 IPATCH_FD_CLOSE(swap_fd); g_unlink(swap_file_name); // unlink old file swap_fd = newfd; // Rename new swap file to swap file name if(g_rename(newname, swap_file_name) == -1) { // If rename fails for some reason, just leave the new file where it is g_warning(_("Failed to rename new swap file from '%s' to '%s'"), newname, swap_file_name); g_free(swap_file_name); // -- free swap file name swap_file_name = newname; // !! takes over allocation newname = NULL; } // Fixup locations for(i = 0, p = swap_list; i < position_array->len; i++, p = p->next) { store = (IpatchSampleStoreSwap *)(p->data); store->location = g_array_index(position_array, guint, i); } G_UNLOCK(swap); // -- unlock swap g_free(newname); // -- free newname g_free(buf); // -- free buffer g_array_free(position_array, TRUE); // -- free array return (TRUE); error: G_UNLOCK(swap); // -- unlock swap IPATCH_FD_CLOSE(newfd); // -- close new swap file g_unlink(newname); // -- unlink new swap file g_free(newname); // -- free newname g_free(buf); // -- free buffer g_array_free(position_array, TRUE); // -- free array return (FALSE); } #ifdef IPATCH_DEBUG /** * ipatch_sample_store_swap_dump: (skip) * * Dump information about sample swap to stdout for debugging. */ void ipatch_sample_store_swap_dump(void) { IpatchSampleStoreSwap *swap_store; SwapRecover *recover; GSList *p; G_LOCK(swap); // ++ lock swap printf("Swap file: %s\n", swap_file_name); printf("Pos=%u Unused=%d RamUse=%d RamMax=%d\n", swap_position, swap_unused_size, swap_ram_used, swap_ram_max); printf("\nSwap Samples:\n"); for(p = swap_list; p; p = p->next) { int sample_format, sample_rate, loop_type, root_note, fine_tune; guint sample_size, loop_start, loop_end; char *title; swap_store = IPATCH_SAMPLE_STORE_SWAP(p->data); g_object_get(swap_store, "title", &title, "sample-size", &sample_size, // ++ allocate title "sample-format", &sample_format, "sample-rate", &sample_rate, "loop-type", &loop_type, "loop-start", &loop_start, "loop-end", &loop_end, "root-note", &root_note, "fine-tune", &fine_tune, NULL); printf(" Store %p: loc=%u title='%s' size=%u fmt=0x%X rate=%d ltype=%d lstart=%u lend=%u root=%d fine=%d\n", swap_store, swap_store->location, title, sample_size, sample_format, sample_rate, loop_type, loop_start, loop_end, root_note, fine_tune); g_free(title); // -- free title } printf("\nRecover Segments:\n"); for(p = swap_recover_loc_list; p; p = p->next) { recover = (SwapRecover *)(p->data); printf("%08X: size=%u\n", recover->location, recover->size); } G_UNLOCK(swap); // -- unlock swap } #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreSwap.h000066400000000000000000000055311400263525300232660ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SAMPLE_STORE_SWAP_H__ #define __IPATCH_SAMPLE_STORE_SWAP_H__ #include #include #include typedef struct _IpatchSampleStoreSwap IpatchSampleStoreSwap; typedef struct _IpatchSampleStoreSwapClass IpatchSampleStoreSwapClass; #define IPATCH_TYPE_SAMPLE_STORE_SWAP \ (ipatch_sample_store_swap_get_type ()) #define IPATCH_SAMPLE_STORE_SWAP(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SAMPLE_STORE_SWAP, \ IpatchSampleStoreSwap)) #define IPATCH_SAMPLE_STORE_SWAP_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SAMPLE_STORE_SWAP, \ IpatchSampleStoreSwapClass)) #define IPATCH_IS_SAMPLE_STORE_SWAP(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SAMPLE_STORE_SWAP)) #define IPATCH_IS_SAMPLE_STORE_SWAP_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SAMPLE_STORE_SWAP)) /* Swap file sample store instance (derived from FILE sample store) */ struct _IpatchSampleStoreSwap { IpatchSampleStore parent_instance; gpointer ram_location; // Pointer to memory location or NULL if stored in file guint location; // Position in file of the sample data (if ram_location is NULL) }; /* Swap file sample store class (derived from FILE sample store) */ struct _IpatchSampleStoreSwapClass { IpatchSampleStoreClass parent_class; }; /** * IPATCH_SAMPLE_STORE_SWAP_UNUSED_FLAG_SHIFT: (skip) */ /* we reserve 1 private flag */ #define IPATCH_SAMPLE_STORE_SWAP_UNUSED_FLAG_SHIFT \ (IPATCH_SAMPLE_STORE_UNUSED_FLAG_SHIFT + 1) GType ipatch_sample_store_swap_get_type(void); void ipatch_set_sample_store_swap_file_name(const char *filename); char *ipatch_get_sample_store_swap_file_name(void); IpatchSample *ipatch_sample_store_swap_new(void); void ipatch_sample_store_swap_close(void); int ipatch_get_sample_store_swap_unused_size(void); void ipatch_set_sample_store_swap_max_memory(int size); int ipatch_get_sample_store_swap_max_memory(void); gboolean ipatch_compact_sample_store_swap(GError **err); #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreVirtual.c000066400000000000000000000323501400263525300237740ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSampleStoreVirtual * @short_description: Virtual sample storage object * @see_also: #IpatchSampleList * @stability: Stable * * A sample store that does in place sample conversions of other samples * using sample lists (#IpatchSampleList). */ #include #include #include "IpatchSampleStoreVirtual.h" #include "ipatch_priv.h" #include "i18n.h" /* properties */ enum { PROP_0, PROP_SAMPLE_LISTS }; static void ipatch_sample_store_virtual_sample_iface_init(IpatchSampleIface *iface); static void ipatch_sample_store_virtual_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_sample_store_virtual_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_sample_store_virtual_finalize(GObject *object); static gboolean ipatch_sample_store_virtual_sample_iface_open(IpatchSampleHandle *handle, GError **err); static void ipatch_sample_store_virtual_sample_iface_close(IpatchSampleHandle *handle); static gboolean ipatch_sample_store_virtual_sample_iface_read(IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err); G_DEFINE_TYPE_WITH_CODE(IpatchSampleStoreVirtual, ipatch_sample_store_virtual, IPATCH_TYPE_SAMPLE_STORE, G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE, ipatch_sample_store_virtual_sample_iface_init)) static void ipatch_sample_store_virtual_sample_iface_init(IpatchSampleIface *iface) { iface->open = ipatch_sample_store_virtual_sample_iface_open; iface->close = ipatch_sample_store_virtual_sample_iface_close; iface->read = ipatch_sample_store_virtual_sample_iface_read; } static void ipatch_sample_store_virtual_class_init(IpatchSampleStoreVirtualClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->finalize = ipatch_sample_store_virtual_finalize; obj_class->get_property = ipatch_sample_store_virtual_get_property; item_class->item_set_property = ipatch_sample_store_virtual_set_property; g_object_class_install_property(obj_class, PROP_SAMPLE_LISTS, g_param_spec_value_array("sample-lists", "Sample lists", "Sample lists", g_param_spec_boxed("value", "value", "value", IPATCH_TYPE_SAMPLE_LIST, G_PARAM_READWRITE), G_PARAM_READWRITE)); } static void ipatch_sample_store_virtual_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchSampleStoreVirtual *store = IPATCH_SAMPLE_STORE_VIRTUAL(object); IpatchSampleList *list; GValueArray *array; guint chan; switch(property_id) { case PROP_SAMPLE_LISTS: array = g_value_get_boxed(value); for(chan = 0; chan < 2; chan++) { if(array && chan < array->n_values) { list = g_value_dup_boxed(g_value_array_get_nth(array, chan)); } else { list = NULL; } ipatch_sample_store_virtual_set_list(store, chan, list); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_sample_store_virtual_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchSampleStoreVirtual *store = IPATCH_SAMPLE_STORE_VIRTUAL(object); GValueArray *array; GValue local_value = { 0 }; int chan; switch(property_id) { case PROP_SAMPLE_LISTS: array = g_value_array_new(0); /* ++ alloc new value array */ for(chan = 0; chan < 2 && store->lists[chan]; chan++) { g_value_init(&local_value, IPATCH_TYPE_SAMPLE_LIST); g_value_set_boxed(&local_value, store->lists[chan]); g_value_array_append(array, &local_value); g_value_unset(&local_value); } g_value_take_boxed(value, array); /* !takes over ownership of array */ break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_sample_store_virtual_init(IpatchSampleStoreVirtual *store) { } static void ipatch_sample_store_virtual_finalize(GObject *object) { IpatchSampleStoreVirtual *vstore = IPATCH_SAMPLE_STORE_VIRTUAL(object); if(vstore->lists[0]) { ipatch_sample_list_free(vstore->lists[0]); } if(vstore->lists[1]) { ipatch_sample_list_free(vstore->lists[1]); } if(G_OBJECT_CLASS(ipatch_sample_store_virtual_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_sample_store_virtual_parent_class)->finalize(object); } } static gboolean ipatch_sample_store_virtual_sample_iface_open(IpatchSampleHandle *handle, GError **err) { IpatchSampleStoreVirtual *store = IPATCH_SAMPLE_STORE_VIRTUAL(handle->sample); int format, channels; g_return_val_if_fail(store->lists[0] != NULL, FALSE); format = ipatch_sample_store_get_format(store); channels = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format); g_return_val_if_fail(channels >= 1 && channels <= 2, FALSE); handle->data1 = GINT_TO_POINTER(format); if(channels == 2) { g_return_val_if_fail(store->lists[1] != NULL, FALSE); handle->data2 = g_malloc(IPATCH_SAMPLE_TRANS_BUFFER_SIZE); /* Allocate stereo interleave buffer */ handle->data3 = GUINT_TO_POINTER(ipatch_sample_format_width(format)); /* Format width in bytes */ } return (TRUE); } static void ipatch_sample_store_virtual_sample_iface_close(IpatchSampleHandle *handle) { g_free(handle->data2); /* Free stereo interleave buffer */ } static gboolean ipatch_sample_store_virtual_sample_iface_read(IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err) { IpatchSampleStoreVirtual *store = IPATCH_SAMPLE_STORE_VIRTUAL(handle->sample); int format = GPOINTER_TO_INT(handle->data1); gpointer interbuf = handle->data2; /* Stereo interleave buffer */ int bytewidth = GPOINTER_TO_INT(handle->data3); guint8 *bbuf, *bleft, *bright; guint16 *wbuf, *wleft, *wright; guint32 *dbuf, *dleft, *dright; guint64 *qbuf, *qleft, *qright; guint block, mi, si; if(!interbuf) /* Store is mono? - Just render it */ { if(!ipatch_sample_list_render(store->lists[0], buf, offset, frames, format, err)) { return (FALSE); } return (TRUE); } /* Stereo virtual store */ /* Max samples that can be interleaved at a time */ block = IPATCH_SAMPLE_TRANS_BUFFER_SIZE / bytewidth / 2; while(frames > 0) { if(block > frames) { block = frames; } if(!ipatch_sample_list_render(store->lists[0], interbuf, offset, block, format, err)) { return (FALSE); } if(!ipatch_sample_list_render(store->lists[1], (guint8 *)interbuf + IPATCH_SAMPLE_TRANS_BUFFER_SIZE / 2, offset, block, format, err)) { return (FALSE); } /* choose interleaving based on sample width */ if(bytewidth == 1) { bbuf = (guint8 *)buf; bleft = (guint8 *)interbuf; bright = (guint8 *)((guint8 *)interbuf + IPATCH_SAMPLE_TRANS_BUFFER_SIZE / 2); for(mi = 0, si = 0; mi < block; mi++, si += 2) { bbuf[si] = bleft[mi]; bbuf[si + 1] = bright[mi]; } buf = bbuf; } else if(bytewidth == 2) { wbuf = (guint16 *)buf; wleft = (guint16 *)interbuf; wright = (guint16 *)((guint8 *)interbuf + IPATCH_SAMPLE_TRANS_BUFFER_SIZE / 2); for(mi = 0, si = 0; mi < block; mi++, si += 2) { wbuf[si] = wleft[mi]; wbuf[si + 1] = wright[mi]; } buf = wbuf; } else if(bytewidth == 3) /* Real 24 bit - Copy as 16 bit word and 1 byte */ { bbuf = (guint8 *)buf; bleft = (guint8 *)interbuf; bright = (guint8 *)((guint8 *)interbuf + IPATCH_SAMPLE_TRANS_BUFFER_SIZE / 2); for(mi = 0, si = 0; mi < block; mi += 3, si += 6) { *(guint16 *)(bbuf + si) = *(guint16 *)(bleft + mi); bbuf[si + 2] = bleft[mi + 2]; *(guint16 *)(bbuf + si + 3) = *(guint16 *)(bright + mi); bbuf[si + 5] = bright[mi + 2]; } buf = bbuf; } else if(bytewidth == 4) { dbuf = (guint32 *)buf; dleft = (guint32 *)interbuf; dright = (guint32 *)((guint8 *)interbuf + IPATCH_SAMPLE_TRANS_BUFFER_SIZE / 2); for(mi = 0, si = 0; mi < block; mi++, si += 2) { dbuf[si] = dleft[mi]; dbuf[si + 1] = dright[mi]; } buf = dbuf; } else if(bytewidth == 8) { qbuf = (guint64 *)buf; qleft = (guint64 *)interbuf; qright = (guint64 *)((guint8 *)interbuf + IPATCH_SAMPLE_TRANS_BUFFER_SIZE / 2); for(mi = 0, si = 0; mi < block; mi++, si += 2) { qbuf[si] = qleft[mi]; qbuf[si + 1] = qright[mi]; } buf = qbuf; } else { g_return_val_if_fail(NOT_REACHED, FALSE); } frames -= block; offset += block; } return (TRUE); } /** * ipatch_sample_store_virtual_new: * * Creates a new virtual sample store. * * Returns: (type IpatchSampleStoreVirtual): New virtual sample store, cast * as a #IpatchSample for convenience. */ IpatchSample * ipatch_sample_store_virtual_new(void) { return (IPATCH_SAMPLE(g_object_new(IPATCH_TYPE_SAMPLE_STORE_VIRTUAL, NULL))); } /** * ipatch_sample_store_virtual_get_list: * @store: Virtual store to get sample list from * @chan: Which channel to get sample list from (0 = mono or left stereo channel, * 1 = right stereo channel). * * Gets a sample list from a virtual sample store. * * Returns: (transfer none): The sample list for the corresponding channel or %NULL if not * assigned. The list is internal and should not be modified or freed and * should be used only as long as @store. */ IpatchSampleList * ipatch_sample_store_virtual_get_list(IpatchSampleStoreVirtual *store, guint chan) { guint chancount; int format; g_return_val_if_fail(IPATCH_IS_SAMPLE_STORE_VIRTUAL(store), NULL); format = ipatch_sample_store_get_format(store); chancount = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format); g_return_val_if_fail(chancount <= 2, NULL); g_return_val_if_fail(chan < chancount, NULL); /* no locking required - lists shouldn't be changed after store is active */ return (store->lists[chan]); } /** * ipatch_sample_store_virtual_set_list: * @store: Virtual store to set sample list of * @chan: Which channel to set sample list of (0 = mono or left stereo channel, * 1 = right stereo channel). * @list: (transfer full): List to assign to virtual store. The allocation is taken over by * the virtual store (if caller would like to continue using it beyond the * life of @store or modify it, it should be duplicated). * * Sets a sample list of a virtual sample store. Can only be assigned before * the sample store is active. The size of @store is set to that of @list. */ void ipatch_sample_store_virtual_set_list(IpatchSampleStoreVirtual *store, guint chan, IpatchSampleList *list) { guint chancount; int format; g_return_if_fail(IPATCH_IS_SAMPLE_STORE_VIRTUAL(store)); format = ipatch_sample_store_get_format(store); chancount = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format); g_return_if_fail(chancount <= 2); g_return_if_fail(chan < chancount); if(store->lists[chan]) { ipatch_sample_list_free(store->lists[chan]); } /* no locking required - lists should only be assigned by a single thread */ store->lists[chan] = list; ((IpatchSampleStore *)store)->size = list->total_size; } libinstpatch-1.1.6/libinstpatch/IpatchSampleStoreVirtual.h000066400000000000000000000051651400263525300240050ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SAMPLE_STORE_VIRTUAL_H__ #define __IPATCH_SAMPLE_STORE_VIRTUAL_H__ #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchSampleStoreVirtual IpatchSampleStoreVirtual; typedef struct _IpatchSampleStoreVirtualClass IpatchSampleStoreVirtualClass; #define IPATCH_TYPE_SAMPLE_STORE_VIRTUAL \ (ipatch_sample_store_virtual_get_type ()) #define IPATCH_SAMPLE_STORE_VIRTUAL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SAMPLE_STORE_VIRTUAL, \ IpatchSampleStoreVirtual)) #define IPATCH_SAMPLE_STORE_VIRTUAL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SAMPLE_STORE_VIRTUAL, \ IpatchSampleStoreVirtualClass)) #define IPATCH_IS_SAMPLE_STORE_VIRTUAL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SAMPLE_STORE_VIRTUAL)) #define IPATCH_IS_SAMPLE_STORE_VIRTUAL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SAMPLE_STORE_VIRTUAL)) /* Virtual sample store instance */ struct _IpatchSampleStoreVirtual { IpatchSampleStore parent_instance; /*< private >*/ IpatchSampleList *lists[2]; /* Edit lists, one per channel (max = stereo currently) */ int access_speed; /* Cached access speed value (0 if not yet calculated) */ }; /* Virtual sample store class */ struct _IpatchSampleStoreVirtualClass { IpatchSampleStoreClass parent_class; }; GType ipatch_sample_store_virtual_get_type(void); IpatchSample *ipatch_sample_store_virtual_new(void); IpatchSampleList * ipatch_sample_store_virtual_get_list(IpatchSampleStoreVirtual *store, guint chan); void ipatch_sample_store_virtual_set_list(IpatchSampleStoreVirtual *store, guint chan, IpatchSampleList *list); #endif libinstpatch-1.1.6/libinstpatch/IpatchSampleTransform.c000066400000000000000000000602231400263525300233040ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSampleTransform * @short_description: Audio format conversion instance * @see_also: * @stability: Stable * * A structure for converting between audio formats (for example the bit width * or number of channels). This structure is initialized with the source and * destination audio formats, multi-channel mapping and conversion buffers. */ #include #include #include #include "IpatchSampleTransform.h" #include "sample.h" #include "ipatch_priv.h" G_LOCK_DEFINE_STATIC(transform_pool); static GList *transform_pool = NULL; static void _ipatch_sample_transform_free_transform(IpatchSampleTransform *data, gpointer user_data); /* ----- Initialization/deinitialization of list ----------------------------*/ /* Initialize transform_pool */ void _ipatch_sample_transform_init(void) { transform_pool = NULL; } /* Free transform_pool */ void _ipatch_sample_transform_deinit(void) { g_list_foreach(transform_pool, (GFunc)_ipatch_sample_transform_free_transform, NULL); g_list_free(transform_pool); } /* free one conv_maps data */ static void _ipatch_sample_transform_free_transform(IpatchSampleTransform *data, gpointer user_data) { ipatch_sample_transform_free(data); } /*------ IpatchSampleTransform object funtions -------------------------------*/ GType ipatch_sample_transform_get_type(void) { static GType type = 0; if(!type) type = g_boxed_type_register_static("IpatchSampleTransform", (GBoxedCopyFunc)ipatch_sample_transform_duplicate, (GBoxedFreeFunc)ipatch_sample_transform_free); return (type); } /** * ipatch_sample_transform_new: * @src_format: Source sample format or 0 to set later * @dest_format: Destination sample format or 0 to set later * @channel_map: Channel mapping (use #IPATCH_SAMPLE_UNITY_CHANNEL_MAP * to map all input channels to the same output channels, see * #IPATCH_SAMPLE_MAP_CHANNEL macro for constructing channel map values) * * Create a new sample transform object. If @src_format and @dest_format are * not 0 then the transform is initialized for the given source and destination * formats, otherwise they are expected to be set later with * ipatch_sample_transform_set_formats(). * * Returns: New allocated sample transform structure which should be freed with * ipatch_sample_transform_free() when done with it. */ IpatchSampleTransform * ipatch_sample_transform_new(int src_format, int dest_format, guint32 channel_map) { IpatchSampleTransform *trans; int i; trans = g_slice_new0(IpatchSampleTransform); /* Setup straight through 1:1 channel mapping */ for(i = 0; i < 8; i++) { trans->channel_map[i] = i; } if(src_format != 0 && dest_format != 0) { ipatch_sample_transform_set_formats(trans, src_format, dest_format, channel_map); } return (trans); } /** * ipatch_sample_transform_free: * @transform: Transform structure * * Free a transform structure as allocated by ipatch_sample_transform_new() and * its allocated resources. */ void ipatch_sample_transform_free(IpatchSampleTransform *transform) { g_return_if_fail(transform != NULL); if(transform->free_buffers) { g_free(transform->buf1); } g_slice_free(IpatchSampleTransform, transform); } /** * ipatch_sample_transform_duplicate: * @transform: Transform structure * * Duplicate a sample transform. * * Returns: New duplicated sample transform structure. * * Since: 1.1.0 */ IpatchSampleTransform * ipatch_sample_transform_duplicate(const IpatchSampleTransform *transform) { IpatchSampleTransform *new; guint32 channel_map = 0; int i; // Convert channel map byte array to guint32 for(i = 0; i < 8; i++) { channel_map |= (transform->channel_map[i] & 0x07) << (i * 3); } new = ipatch_sample_transform_new(transform->src_format, transform->dest_format, channel_map); if(transform->max_frames > 0) { ipatch_sample_transform_alloc(new, transform->max_frames); } return (new); } /** * ipatch_sample_transform_init: (skip) * @transform: Sample transform to initialize * * Initialize a sample transform structure. Usually only used to initialize transform * structures allocated on the stack, which is done to avoid mallocs. */ void ipatch_sample_transform_init(IpatchSampleTransform *transform) { int i; g_return_if_fail(transform != NULL); memset(transform, 0, sizeof(IpatchSampleTransform)); /* Setup straight through 1:1 channel mapping */ for(i = 0; i < 8; i++) { transform->channel_map[i] = i; } } /** * ipatch_sample_transform_pool_acquire: (skip) * @src_format: Source sample format * @dest_format: Destination sample format * @channel_map: Channel mapping (use #IPATCH_SAMPLE_UNITY_CHANNEL_MAP * to map all input channels to the same output channels, see * #IPATCH_SAMPLE_MAP_CHANNEL macro for constructing channel map values) * * Get an unused sample transform object from the sample transform pool. * Used for quickly getting a transform object for temporary use without the * overhead of allocating one. Note though, that if no more transforms exist * in the pool an allocation will occur. * * Returns: Sample transform object. Should be released after use with * ipatch_sample_transform_pool_release(). */ IpatchSampleTransform * ipatch_sample_transform_pool_acquire(int src_format, int dest_format, guint32 channel_map) { IpatchSampleTransform *trans = NULL; g_return_val_if_fail(ipatch_sample_format_transform_verify(src_format, dest_format, channel_map), NULL); G_LOCK(transform_pool); if(transform_pool) { trans = (IpatchSampleTransform *)(transform_pool->data); transform_pool = g_list_delete_link(transform_pool, transform_pool); } G_UNLOCK(transform_pool); if(!trans) { trans = ipatch_sample_transform_new(src_format, dest_format, channel_map); ipatch_sample_transform_alloc_size(trans, IPATCH_SAMPLE_TRANS_BUFFER_SIZE); } else { ipatch_sample_transform_set_formats(trans, src_format, dest_format, channel_map); } return (trans); } /** * ipatch_sample_transform_pool_release: (skip) * @transform: Sample transform * * Release a sample transform object, returned by * ipatch_sample_transform_pool_grab(), back to the transform pool. */ void ipatch_sample_transform_pool_release(IpatchSampleTransform *transform) { g_return_if_fail(transform != NULL); G_LOCK(transform_pool); transform_pool = g_list_prepend(transform_pool, transform); G_UNLOCK(transform_pool); } /** * ipatch_sample_transform_set_formats: * @transform: Transform object * @src_format: Source audio format to convert from * @dest_format: Destination audio format to convert to * @channel_map: Channel mapping (use #IPATCH_SAMPLE_UNITY_CHANNEL_MAP * to map all input channels to the same output channels, see * #IPATCH_SAMPLE_MAP_CHANNEL macro for constructing channel map values) * * Initialize a sample transform object for converting from * @src_format to @dest_format. */ void ipatch_sample_transform_set_formats(IpatchSampleTransform *transform, int src_format, int dest_format, guint32 channel_map) { guint buf1_max_frame, buf2_max_frame, func_count; int i, chans; g_return_if_fail(transform != NULL); g_return_if_fail(ipatch_sample_format_transform_verify(src_format, dest_format, channel_map)); transform->src_format = src_format; transform->dest_format = dest_format; /* Convert channel map integer to byte array */ for(i = 0; i < 8; i++) { transform->channel_map[i] = (channel_map >> (i * 3)) & 0x07; } transform->func_count = 0; if(src_format == dest_format) /* Shortcut identical formats */ { chans = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(src_format); /* Straight through channel mapping? */ for(i = 0; i < chans; i++) if(transform->channel_map[i] != i) { break; } if(i == chans) { transform->buf1_max_frame = ipatch_sample_format_size(src_format); transform->buf2_max_frame = 0; transform->max_frames = transform->combined_size ? transform->combined_size / transform->buf1_max_frame : 0; return; } } func_count = ipatch_sample_get_transform_funcs (src_format, dest_format, channel_map, &buf1_max_frame, &buf2_max_frame, transform->funcs); transform->buf1_max_frame = buf1_max_frame; transform->buf2_max_frame = buf2_max_frame; transform->func_count = func_count; /* update max frames if buffer is already assigned */ if(transform->combined_size) { transform->max_frames = transform->combined_size / (buf1_max_frame + buf2_max_frame); transform->buf2 = (guint8 *)transform->buf1 + (transform->buf1_max_frame * transform->max_frames); } else { transform->max_frames = 0; } } /** * ipatch_sample_transform_alloc: * @transform: Sample transform object * @frames: Number of frames to allocate for (max frames processed in batch) * * Allocate buffers for transforming between two audio formats, for * which @transform has previously been initialized for with * ipatch_sample_transform_new() or ipatch_sample_transform_set_formats(). * * Note: Assigning buffers with this function allows sample formats to be * changed without re-assigning the buffers. */ void ipatch_sample_transform_alloc(IpatchSampleTransform *transform, guint frames) { g_return_if_fail(transform != NULL); g_return_if_fail(frames > 0); g_return_if_fail(transform->src_format != 0); if(transform->free_buffers) { g_free(transform->buf1); } transform->combined_size = (transform->buf1_max_frame + transform->buf2_max_frame) * frames; transform->buf1 = g_malloc(transform->combined_size); transform->buf2 = (guint8 *)transform->buf1 + (transform->buf1_max_frame * frames); transform->free_buffers = TRUE; transform->max_frames = frames; } /** * ipatch_sample_transform_alloc_size: * @transform: Sample transform object * @size: Maximum total size in bytes of allocation for both transform buffers. * * Like ipatch_sample_transform_alloc() but allocates buffers based on a * maximum size and returns the maximum number of sample frames which can be * converted at a time using this size. Another difference is that conversion * formats do not need to be set before calling this function. * * Returns: The maximum number of frames that can be converted at a time for * the given @size of transform buffers. If conversion formats have not yet * been set, then this function returns 0. */ int ipatch_sample_transform_alloc_size(IpatchSampleTransform *transform, guint size) { g_return_val_if_fail(transform != NULL, 0); g_return_val_if_fail(size > 32, 0); /* just a somewhat sane value */ if(transform->free_buffers) { g_free(transform->buf1); } transform->combined_size = size; transform->buf1 = g_malloc(size); transform->free_buffers = TRUE; transform->buf2 = NULL; transform->max_frames = 0; /* update buffer split if formats already assigned */ if(transform->src_format && transform->dest_format) { transform->max_frames = size / (transform->buf1_max_frame + transform->buf2_max_frame); transform->buf2 = (guint8 *)transform->buf1 + (transform->buf1_max_frame * transform->max_frames); } return (transform->max_frames); } /** * ipatch_sample_transform_free_buffers: * @transform: Sample transform object * * Free sample transform buffers. */ void ipatch_sample_transform_free_buffers(IpatchSampleTransform *transform) { g_return_if_fail(transform != NULL); if(transform->free_buffers) { g_free(transform->buf1); } transform->buf1 = NULL; transform->buf2 = NULL; transform->combined_size = 0; transform->max_frames = 0; } /** * ipatch_sample_transform_set_buffers_size: * @transform: Sample transform object * @buf: (array length=size) (element-type guint8) (transfer none): Buffer to * assign for transforming sample formats * @size: Size of @buf in bytes * * Assign transform buffers using a single buffer of a * specific size in bytes and determine optimal division for source and * destination buffers. Conversion formats must not be set before calling this * function (can be set later). * * Returns: The maximum number of frames that can be converted at a time for * the given @size of transform buffers. If conversion formats have not yet * been set, then this function returns 0. */ guint ipatch_sample_transform_set_buffers_size(IpatchSampleTransform *transform, gpointer buf, guint size) { g_return_val_if_fail(transform != NULL, 0); g_return_val_if_fail(buf != NULL, 0); g_return_val_if_fail(size > 32, 0); /* some slightly sane value */ if(transform->free_buffers) { g_free(transform->buf1); } transform->buf1 = buf; transform->free_buffers = FALSE; transform->combined_size = size; transform->buf2 = NULL; transform->max_frames = 0; /* update buffer split if formats already assigned */ if(transform->src_format && transform->dest_format) { transform->max_frames = size / (transform->buf1_max_frame + transform->buf2_max_frame); transform->buf2 = (guint8 *)transform->buf1 + (transform->buf1_max_frame * transform->max_frames); } return (transform->max_frames); } /** * ipatch_sample_transform_get_buffers: (skip) * @transform: Sample transform object * @buf1: (out) (optional): First buffer or %NULL to ignore * @buf2: (out) (optional): Second buffer or %NULL to ignore * * Get the sample data buffers in a sample transform object. */ void ipatch_sample_transform_get_buffers(IpatchSampleTransform *transform, gpointer *buf1, gpointer *buf2) { g_return_if_fail(transform != NULL); if(buf1) { *buf1 = transform->buf1; } if(buf2) { *buf2 = transform->buf2; } } /** * ipatch_sample_transform_get_frame_sizes: * @transform: Initialized transform object * @buf1_size: (out) (optional): Maximum frame size for buffer 1 or %NULL to ignore * @buf2_size: (out) (optional): Maximum frame size for buffer 2 or %NULL to ignore * * Get max frame sizes for transform buffers. When transforming audio the * first buffer must be at least frames * buf1_size bytes in size and the * second buffer must be at least frames * buf2_size, where frames is the * max number of frames to convert in batch. */ void ipatch_sample_transform_get_frame_sizes(IpatchSampleTransform *transform, guint *buf1_size, guint *buf2_size) { g_return_if_fail(transform != NULL); if(buf1_size) { *buf1_size = transform->buf1_max_frame; } if(buf2_size) { *buf2_size = transform->buf2_max_frame; } } /** * ipatch_sample_transform_get_max_frames: * @transform: Sample transform object * * Get the maximum frames that the @transform object can convert at a time. * * Returns: Max number of frames that can be converted at a time, 0 if buffers * have not been allocated yet. */ guint ipatch_sample_transform_get_max_frames(IpatchSampleTransform *transform) { g_return_val_if_fail(transform != NULL, 0); return (transform->max_frames); } /** * ipatch_sample_transform_convert: (skip) * @transform: An initialized sample transform object * @src: Audio source buffer (@frames X the source frame size in bytes), can * be %NULL to use internal buffer (provided @frames is less than or equal to * the max frames that can be converted at a time), in which case buf1 of * @transform should already be loaded with source data. * @dest: Converted audio destination buffer (@frames X the destination frame * size in bytes), can be %NULL to use internal buffer (provided @frames is * less than or equal to the max frames that can be converted at a time). * @frames: Number of audio frames to convert * * Convert an arbitrary number of audio frames from user provided buffers, * contrary to ipatch_sample_transform_convert_single() which uses internal * buffers and converts a max of 1 buffer at a time. The sample formats * should already be assigned and internal buffers assigned or allocated. * * Returns: (transfer none): Pointer to converted data. Will be @dest if it was not %NULL or * internal buffer containing the converted data otherwise. */ gpointer ipatch_sample_transform_convert(IpatchSampleTransform *transform, gconstpointer src, gpointer dest, guint frames) { int i, func_count; guint block_size; gpointer buf1, buf2; int src_frame_size, dest_frame_size, srcchan; g_return_val_if_fail(transform != NULL, NULL); g_return_val_if_fail(frames > 0, NULL); g_return_val_if_fail(transform->buf1 != NULL, NULL); g_return_val_if_fail(transform->buf2 != NULL, NULL); g_return_val_if_fail(transform->max_frames > 0, NULL); g_return_val_if_fail((src && dest) || frames <= transform->max_frames, NULL); block_size = transform->max_frames; func_count = transform->func_count; buf1 = transform->buf1; buf2 = transform->buf2; if(!src) { src = buf1; } src_frame_size = ipatch_sample_format_size(transform->src_format); srcchan = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(transform->src_format); dest_frame_size = ipatch_sample_format_size(transform->dest_format); if(func_count == 0) /* same format? Just copy it */ { if(dest) { memcpy(dest, src, frames * src_frame_size); return (dest); } else { return ((gpointer)src); } } while(frames > 0) { if(block_size > frames) { block_size = frames; } transform->frames = block_size; transform->samples = block_size * srcchan; /* execute first transform function with src buffer directly */ transform->buf1 = (gpointer)src; transform->buf2 = (func_count == 1 && dest) ? dest : buf2; (*transform->funcs[0])(transform); /* execute remaining transform functions */ for(i = 1; i < func_count; i++) { if(i & 1) { transform->buf1 = buf2; transform->buf2 = (i == (func_count - 1) && dest) ? dest : buf1; } else { transform->buf1 = buf1; transform->buf2 = (i == (func_count - 1) && dest) ? dest : buf2; } (*transform->funcs[i])(transform); } frames -= block_size; src = (guint8 *)src + (block_size * src_frame_size); if(dest) { dest = (guint8 *)dest + (block_size * dest_frame_size); } } transform->buf1 = buf1; transform->buf2 = buf2; if(dest) { return (dest); } else { return ((func_count & 1) ? buf2 : buf1); } } /** * ipatch_sample_transform_convert_sizes: (rename-to ipatch_sample_transform_convert) * @transform: An initialized sample transform object * @src: (array length=src_size) (element-type guint8) (transfer none): Audio source buffer * @src_size: Size of src buffer data to convert (in bytes, must be a multiple of the source format) * @dest_size: (out): Location to store size of returned converted audio buffer * * Convert an arbitrary number of audio frames from user provided buffer. * This is like ipatch_sample_transform_convert() but friendly to GObject * introspection and the destination conversion buffer is allocated. The sample formats * should already be assigned and internal buffers assigned or allocated. * * Returns: (array length=dest_size) (element-type guint8) (transfer full): Newly allocated * converted audio data buffer. Free with g_free() when done with it. * * Since: 1.1.0 */ gpointer ipatch_sample_transform_convert_sizes(IpatchSampleTransform *transform, gconstpointer src, guint src_size, guint *dest_size) { int src_frame_size, dest_frame_size, frames; gpointer destbuf; g_return_val_if_fail(transform != NULL, NULL); g_return_val_if_fail(src_size > 0, NULL); src_frame_size = ipatch_sample_format_size(transform->src_format); g_return_val_if_fail(src_size % src_frame_size == 0, NULL); dest_frame_size = ipatch_sample_format_size(transform->dest_format); g_return_val_if_fail(dest_frame_size > 0, NULL); frames = src_size / src_frame_size; destbuf = g_malloc(dest_frame_size * frames); // ++ alloc conversion buffer if(dest_size) { *dest_size = dest_frame_size * frames; } if(!ipatch_sample_transform_convert(transform, src, destbuf, frames)) { g_free(destbuf); // -- free buffer on error return (NULL); } return (destbuf); // !! caller takes over converted buffer } /** * ipatch_sample_transform_convert_single: (skip) * @transform: An initialized sample transform object * @frames: Number of frames to convert (should be less than or equal to the * maximum frames which can be converted at a time * (see ipatch_sample_transform_get_max_frames()). * * Convert the format of a single buffer of audio. The @transform object must * have had its sample formats set and buffers assigned or allocated. Use * ipatch_sample_transform_convert() to convert from one buffer to another * regardless of the number of frames. * * Returns: (transfer none): Pointer to converted audio data (buffer is internal to @transform * object). */ gpointer ipatch_sample_transform_convert_single(IpatchSampleTransform *transform, guint frames) { gpointer temp; int i, count; g_return_val_if_fail(transform != NULL, NULL); g_return_val_if_fail(frames > 0 && frames <= transform->max_frames, NULL); g_return_val_if_fail(transform->buf1 != NULL, NULL); g_return_val_if_fail(transform->buf2 != NULL, NULL); transform->frames = frames; transform->samples = frames; /* multiply frames by number of channels to get # of samples */ transform->samples *= IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(transform->src_format); count = transform->func_count; /* execute each transform function */ for(i = 0; i < count; i++) { (*transform->funcs[i])(transform); /* swap buffer pointers */ temp = transform->buf1; transform->buf1 = transform->buf2; transform->buf2 = temp; } /* swap 1 more time if odd number of functions to set buffer order right */ if(count & 1) { temp = transform->buf1; transform->buf1 = transform->buf2; transform->buf2 = temp; return (transform->buf2); } else { return (transform->buf1); } } libinstpatch-1.1.6/libinstpatch/IpatchSampleTransform.h000066400000000000000000000104441400263525300233110ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SAMPLE_TRANSFORM_H__ #define __IPATCH_SAMPLE_TRANSFORM_H__ #include #include typedef struct _IpatchSampleTransform IpatchSampleTransform; /** * IpatchSampleTransformFunc: * @transform: Sample transform object * * Audio conversion handler prototype. Handler will modify * samples of @transform if number of samples * changes (a change in the number of channels occurs). */ typedef void (*IpatchSampleTransformFunc)(IpatchSampleTransform *transform); #include /* sample transform object */ struct _IpatchSampleTransform { guint16 src_format; /* source sample format */ guint16 dest_format; /* destination sample format */ guint8 channel_map[8]; /* channel mapping for multi-channel audio */ guint8 buf1_max_frame; /* max bytes per frame for buf1 */ guint8 buf2_max_frame; /* max bytes per frame for buf2 */ guint8 func_count; /* number of functions in funcs array */ guint8 free_buffers; /* set to TRUE if buffers need to be freed */ guint max_frames; /* max frames that can be converted in batch */ guint frames; /* number of frames to transform */ guint samples; /* # of samples for current transform func (not frames!)*/ gpointer buf1; /* buffer 1 (first input) */ gpointer buf2; /* buffer 2 */ guint combined_size; /* size in bytes of both buffers (if combined) */ gpointer reserved1; /* Reserved field */ gpointer reserved2; /* Reserved field */ /* array of transform funcs */ IpatchSampleTransformFunc funcs[IPATCH_SAMPLE_MAX_TRANSFORM_FUNCS]; }; GType ipatch_sample_transform_get_type(void); IpatchSampleTransform *ipatch_sample_transform_new (int src_format, int dest_format, guint32 channel_map); void ipatch_sample_transform_free(IpatchSampleTransform *transform); IpatchSampleTransform *ipatch_sample_transform_duplicate(const IpatchSampleTransform *transform); void ipatch_sample_transform_init(IpatchSampleTransform *transform); IpatchSampleTransform *ipatch_sample_transform_pool_acquire (int src_format, int dest_format, guint32 channel_map); void ipatch_sample_transform_pool_release(IpatchSampleTransform *transform); void ipatch_sample_transform_set_formats(IpatchSampleTransform *transform, int src_format, int dest_format, guint32 channel_map); void ipatch_sample_transform_alloc(IpatchSampleTransform *transform, guint frames); int ipatch_sample_transform_alloc_size(IpatchSampleTransform *transform, guint size); void ipatch_sample_transform_free_buffers(IpatchSampleTransform *transform); guint ipatch_sample_transform_set_buffers_size(IpatchSampleTransform *transform, gpointer buf, guint size); void ipatch_sample_transform_get_buffers(IpatchSampleTransform *transform, gpointer *buf1, gpointer *buf2); void ipatch_sample_transform_get_frame_sizes(IpatchSampleTransform *transform, guint *buf1_size, guint *buf2_size); guint ipatch_sample_transform_get_max_frames(IpatchSampleTransform *transform); gpointer ipatch_sample_transform_convert(IpatchSampleTransform *transform, gconstpointer src, gpointer dest, guint frames); gpointer ipatch_sample_transform_convert_sizes(IpatchSampleTransform *transform, gconstpointer src, guint src_size, guint *dest_size); gpointer ipatch_sample_transform_convert_single(IpatchSampleTransform *transform, guint frames); #endif libinstpatch-1.1.6/libinstpatch/IpatchSndFile.c000066400000000000000000000214141400263525300215120ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchSndFile * @short_description: libsndfile file object * @see_also: * @stability: Stable * * Object type for libsndfile audio file identification. */ #include #include #include #include #include "IpatchSndFile.h" #include "sample.h" #include "ipatch_priv.h" #include "i18n.h" #include "misc.h" static gboolean ipatch_snd_file_identify(IpatchFile *file, IpatchFileHandle *handle, GError **err); G_DEFINE_TYPE(IpatchSndFile, ipatch_snd_file, IPATCH_TYPE_FILE) /* Get type of dynamic libsndfile file format enum (register it as needed) */ GType ipatch_snd_file_format_get_type(void) { static GType type = 0; if(!type) { GEnumValue *values; SF_FORMAT_INFO finfo; int major_count; int value_index = 0; int i; sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof(int)); values = g_new(GEnumValue, major_count + 1); for(i = 0; i < major_count; i++) { finfo.format = i; sf_command(NULL, SFC_GET_FORMAT_MAJOR, &finfo, sizeof(finfo)); /* Skip RAW format since we use IpatchSampleStoreFile instead, for more flexibility */ if(finfo.format == SF_FORMAT_RAW) { continue; } values[value_index].value = finfo.format; values[value_index].value_name = finfo.extension; values[value_index].value_nick = finfo.extension; value_index++; } values[value_index].value = 0; values[value_index].value_name = NULL; values[value_index].value_nick = NULL; type = g_enum_register_static("IpatchSndFileFormat", values); } return (type); } /* Get type of dynamic libsndfile file sub format enum (register it as needed) */ GType ipatch_snd_file_sub_format_get_type(void) { static GType type = 0; char *name, *s; if(!type) { GEnumValue *values; SF_FORMAT_INFO sinfo; int subtype_count; int value_index = 0; int i; sf_command(NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &subtype_count, sizeof(int)); values = g_new(GEnumValue, subtype_count + 1); for(i = 0; i < subtype_count; i++) { sinfo.format = i; sf_command(NULL, SFC_GET_FORMAT_SUBTYPE, &sinfo, sizeof(sinfo)); name = g_ascii_strdown(sinfo.name, -1); /* ++ alloc forever */ /* Replace spaces and '.' with dashes */ for(s = name; *s; s++) if(*s == ' ' || *s == '.') { *s = '-'; } values[value_index].value = sinfo.format; values[value_index].value_name = name; values[value_index].value_nick = name; value_index++; } values[value_index].value = 0; values[value_index].value_name = NULL; values[value_index].value_nick = NULL; type = g_enum_register_static("IpatchSndFileSubFormat", values); } return (type); } static void ipatch_snd_file_class_init(IpatchSndFileClass *klass) { IpatchFileClass *file_class = IPATCH_FILE_CLASS(klass); file_class->identify = ipatch_snd_file_identify; /* Set to last execution (subtract another 100, since we really want to be last) */ file_class->identify_order = IPATCH_FILE_IDENTIFY_ORDER_LAST - 100; } static void ipatch_snd_file_init(IpatchSndFile *file) { } /* Identify if this file format is known by libsndfile */ static gboolean ipatch_snd_file_identify(IpatchFile *file, IpatchFileHandle *handle, GError **err) { SNDFILE *sfhandle; SF_INFO info = { 0 }; char *filename; filename = ipatch_file_get_name(file); /* ++ alloc file name */ if(!filename) { return (FALSE); } sfhandle = sf_open(filename, SFM_READ, &info); if(sfhandle) { sf_close(sfhandle); } g_free(filename); /* -- free file name */ return (sfhandle != NULL); } /** * ipatch_snd_file_new: * * Create a new libsndfile file object. * * Returns: New libsndfile file object (derived from IpatchFile) with a * reference count of 1. Caller owns the reference and removing it will * destroy the item. */ IpatchSndFile * ipatch_snd_file_new(void) { return (IPATCH_SND_FILE(g_object_new(IPATCH_TYPE_SND_FILE, NULL))); } /** * ipatch_snd_file_format_get_sub_formats: * @format: (type IpatchSndFileFormat): "IpatchSndFileFormat" GEnum to get sub formats of * @size: (out): Location to store size of returned sub formats array * * Get supported sub formats of a given libsndfile format. * * Returns: (array length=size): Newly allocated list of sub format * enum values or %NULL if @format is invalid */ int * ipatch_snd_file_format_get_sub_formats(int format, guint *size) { SF_FORMAT_INFO info; SF_INFO sfinfo; GArray *array; int subtype_count, s; if(size) { *size = 0; /* In case of error */ } g_return_val_if_fail(size != NULL, NULL); format &= SF_FORMAT_TYPEMASK; /* Mask out everything but file type */ array = g_array_new(FALSE, FALSE, sizeof(int)); /* ++ alloc array */ memset(&sfinfo, 0, sizeof(sfinfo)); sf_command(NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &subtype_count, sizeof(int)); sfinfo.channels = 1; for(s = 0; s < subtype_count; s++) { info.format = s; sf_command(NULL, SFC_GET_FORMAT_SUBTYPE, &info, sizeof(info)); sfinfo.format = format | info.format; if(sf_format_check(&sfinfo)) { g_array_append_val(array, info.format); } } *size = array->len; return ((int *)g_array_free(array, FALSE)); /* !! caller takes over alloc */ } /* FIXME-GIR: @file_format accepts -1 as well! */ /** * ipatch_snd_file_sample_format_to_sub_format: * @sample_format: libinstpatch sample format (see #sample) * @file_format: libsndfile format GEnum "IpatchSndFileFormat" value or -1 to * not limit sub formats to a given file format. * * Get the optimal libsndfile sub format for a libinstpatch sample format. The * returned value may not be an exact equivalent, in the case of unsigned * sample data with bit widths greater than 8, but will return the optimal * format in those cases. If @file_format is not -1 then the resulting sub * format is guaranteed to be valid for it. * * Returns: Optimal libsndfile sub format enum value or -1 on error (invalid * @sample_format). */ int ipatch_snd_file_sample_format_to_sub_format(int sample_format, int file_format) { int sub_format; int *formats; guint i, size; g_return_val_if_fail(ipatch_sample_format_verify(sample_format), -1); switch(IPATCH_SAMPLE_FORMAT_GET_WIDTH(sample_format)) { case IPATCH_SAMPLE_8BIT: sub_format = SF_FORMAT_PCM_S8; break; case IPATCH_SAMPLE_16BIT: sub_format = SF_FORMAT_PCM_16; break; case IPATCH_SAMPLE_24BIT: case IPATCH_SAMPLE_REAL24BIT: sub_format = SF_FORMAT_PCM_24; break; case IPATCH_SAMPLE_32BIT: sub_format = SF_FORMAT_PCM_32; break; case IPATCH_SAMPLE_FLOAT: sub_format = SF_FORMAT_FLOAT; break; case IPATCH_SAMPLE_DOUBLE: sub_format = SF_FORMAT_DOUBLE; break; default: sub_format = SF_FORMAT_PCM_16; break; } if(file_format) { /* ++ alloc array of valid sub formats for this file format */ formats = ipatch_snd_file_format_get_sub_formats(file_format, &size); if(!formats) { return (-1); /* Invalid file_format value */ } for(i = 0; i < size; i++) if(formats[i] == sub_format) { break; } if(i == size) { sub_format = formats[0]; /* sub format not found? Just use first one. FIXME? */ } g_free(formats); /* -- free formats array */ } return (sub_format); } libinstpatch-1.1.6/libinstpatch/IpatchSndFile.h000066400000000000000000000066041400263525300215230ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_SND_FILE_H__ #define __IPATCH_SND_FILE_H__ #include #include #include #include typedef struct _IpatchSndFile IpatchSndFile; typedef struct _IpatchSndFileClass IpatchSndFileClass; #define IPATCH_TYPE_SND_FILE (ipatch_snd_file_get_type ()) #define IPATCH_SND_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_SND_FILE, IpatchSndFile)) #define IPATCH_SND_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_SND_FILE, \ IpatchSndFileClass)) #define IPATCH_IS_SND_FILE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_SND_FILE)) #define IPATCH_IS_SND_FILE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_SND_FILE)) #define IPATCH_SND_FILE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_SND_FILE, \ IpatchSndFileClass)) #define IPATCH_TYPE_SND_FILE_FORMAT \ (ipatch_snd_file_format_get_type()) #define IPATCH_TYPE_SND_FILE_SUB_FORMAT \ (ipatch_snd_file_sub_format_get_type()) /** * IpatchSndFileEndian: * @IPATCH_SND_FILE_ENDIAN_FILE: Use the default endian for the file format * @IPATCH_SND_FILE_ENDIAN_LITTLE: Little endian byte order * @IPATCH_SND_FILE_ENDIAN_BIG: Big endian byte order * @IPATCH_SND_FILE_ENDIAN_CPU: Native CPU byte order * * Endian byte order libsndfile enum. */ typedef enum { IPATCH_SND_FILE_ENDIAN_FILE, IPATCH_SND_FILE_ENDIAN_LITTLE, IPATCH_SND_FILE_ENDIAN_BIG, IPATCH_SND_FILE_ENDIAN_CPU } IpatchSndFileEndian; /** * IPATCH_SND_FILE_DEFAULT_FORMAT: * * Default file format enum for #IPATCH_TYPE_SND_FILE_FORMAT. */ #define IPATCH_SND_FILE_DEFAULT_FORMAT SF_FORMAT_WAV /** * IPATCH_SND_FILE_DEFAULT_SUB_FORMAT: * * Default file sub format enum for #IPATCH_TYPE_SND_FILE_SUB_FORMAT. */ #define IPATCH_SND_FILE_DEFAULT_SUB_FORMAT SF_FORMAT_PCM_16 /** * IPATCH_SND_FILE_DEFAULT_ENDIAN: * * Default endian byte order from enum #IPATCH_TYPE_SND_FILE_ENDIAN. */ #define IPATCH_SND_FILE_DEFAULT_ENDIAN IPATCH_SND_FILE_ENDIAN_FILE /* DLS file object (derived from IpatchFile) */ struct _IpatchSndFile { IpatchFile parent_instance; }; /* DLS file class (derived from IpatchFile) */ struct _IpatchSndFileClass { IpatchFileClass parent_class; }; GType ipatch_snd_file_get_type(void); IpatchSndFile *ipatch_snd_file_new(void); GType ipatch_snd_file_format_get_type(void); GType ipatch_snd_file_sub_format_get_type(void); int *ipatch_snd_file_format_get_sub_formats(int format, guint *size); int ipatch_snd_file_sample_format_to_sub_format(int sample_format, int file_format); #endif libinstpatch-1.1.6/libinstpatch/IpatchState.c000066400000000000000000000506131400263525300212510ustar00rootroot00000000000000/* * IpatchState.c - Item state (undo/redo) system * * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #include #include #include #include "IpatchState.h" #include "ipatch_priv.h" #define ERRMSG_NO_ACTIVE_STATE_GROUP "No active state group!" static void ipatch_state_class_init(IpatchStateClass *klass); static void ipatch_state_init(IpatchState *state); static void ipatch_state_finalize(GObject *gobject); static gboolean tree_traverse_object_unref(GNode *node, gpointer data); static gboolean traverse_set_depends(GNode *node, gpointer data); static gboolean traverse_is_dependent(GNode *node, gpointer data); static gboolean traverse_unset_depends(GNode *node, gpointer data); static void traverse_undo(IpatchStateItem *item, IpatchStateItem *last_depend, IpatchStateItem *last_nondep, IpatchState *state); gpointer state_parent_class = NULL; GType ipatch_state_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchStateClass), NULL, NULL, (GClassInitFunc) ipatch_state_class_init, NULL, NULL, sizeof(IpatchState), 0, (GInstanceInitFunc) ipatch_state_init, }; item_type = g_type_register_static(IPATCH_TYPE_LOCK, "IpatchState", &item_info, 0); } return (item_type); } static void ipatch_state_class_init(IpatchStateClass *klass) { GObjectClass *gobj_class = G_OBJECT_CLASS(klass); state_parent_class = g_type_class_peek_parent(klass); gobj_class->finalize = ipatch_state_finalize; } static void ipatch_state_init(IpatchState *state) { state->root = g_node_new(NULL); /* create root state NULL tree node */ state->position = state->root; state->group_root = g_node_new(NULL); /* create root group NULL node */ state->active_group_key = g_private_new((GDestroyNotify)g_object_unref); } static void ipatch_state_finalize(GObject *gobject) { IpatchState *state = IPATCH_STATE(gobject); IPATCH_LOCK_WRITE(state); /* unref all IpatchStates in tree */ g_node_traverse(state->root, G_IN_ORDER, G_TRAVERSE_ALL, -1, tree_traverse_object_unref, NULL); state->root = NULL; state->position = NULL; /* unref all IpatchStateGroups in group tree */ g_node_traverse(state->group_root, G_IN_ORDER, G_TRAVERSE_ALL, -1, tree_traverse_object_unref, NULL); state->group_root = NULL; IPATCH_UNLOCK_WRITE(state); if(G_OBJECT_CLASS(state_parent_class)->finalize) { G_OBJECT_CLASS(state_parent_class)->finalize(gobject); } } /* unref all GObjects in a tree before murdering it */ static gboolean tree_traverse_object_unref(GNode *node, gpointer data) { GObject *obj = (GObject *)(node->data); g_node_unlink(node); /* unlink the node from the tree */ if(obj) { g_object_unref(obj); /* -- unref object (object destroys node) */ } return (FALSE); } /** * ipatch_state_new: * * Create a new state history object. Returned object has a ref count of 1 * which the caller owns. * * Returns: The new state history object. */ IpatchState * ipatch_state_new(void) { return (g_object_new(IPATCH_TYPE_STATE, NULL)); } /** * ipatch_state_begin_group: * @state: State history object * @descr: Description of group action * * Start a state group. State groups are used to group multiple actions. * Each thread has its own active group, if this function is called without * ending a previous group, a nested group is started. There is no limit to * the depth of group nesting. */ void ipatch_state_begin_group(IpatchState *state, const char *descr) { IpatchStateGroup *group, *active; GNode *parent, *n; g_return_if_fail(IPATCH_IS_STATE(state)); group = g_object_new(IPATCH_TYPE_STATE_GROUP, NULL); /* ++ ref group */ if(descr) { group->descr = g_strdup(descr); } /* get calling thread's active group */ active = g_private_get(state->active_group_key); if(active) { parent = active->node; /* nest new group */ } else { parent = state->group_root; /* top level group */ } /* group tree takes control of group creator's ref */ IPATCH_LOCK_WRITE(state); n = g_node_prepend_data(parent, group); group->node = n; IPATCH_UNLOCK_WRITE(state); g_object_ref(group); /* ++ ref group for active group private var */ g_private_set(state->active_group_key, group); } /** * ipatch_state_end_group: * @state: State history object * * End the current active state group. Causes the next nested group parent * to become active or the active group to be deactivated if there are no * more nested group parents. */ void ipatch_state_end_group(IpatchState *state) { IpatchStateGroup *active; GNode *node; g_return_if_fail(IPATCH_IS_STATE(state)); active = g_private_get(state->active_group_key); if(!active) { g_critical(ERRMSG_NO_ACTIVE_STATE_GROUP); return; } node = active->node->parent; /* move up group tree */ if(node && !node->data) { node = NULL; /* root dummy node? */ } if(node) { g_object_ref(node->data); /* ++ ref group obj for private var */ } g_private_set(state->active_group_key, node->data); } /** * ipatch_state_set_active_group: * @state: State history object * @group: (nullable): State group or %NULL to deactivate grouping * * Sets the active state group for the calling thread in a state history * object or deactivates grouping if @group is NULL. */ void ipatch_state_set_active_group(IpatchState *state, IpatchStateGroup *group) { g_return_if_fail(IPATCH_IS_STATE(state)); g_return_if_fail(!group || IPATCH_IS_STATE_GROUP(group)); if(group) { g_object_ref(group); /* ++ ref group object for private var */ } g_private_set(state->active_group_key, group); } /** * ipatch_state_get_active_group: * @state: State history object * * Get the current active state group for the calling thread. The ref count of * the returned state group is incremented and the caller is responsible for * removing it with g_object_unref when finished. * * Returns: Ipatch state group object or %NULL if no groups active. Remember to * unref it when finished using it. */ IpatchStateGroup * ipatch_state_get_active_group(IpatchState *state) { IpatchStateGroup *active; g_return_val_if_fail(IPATCH_IS_STATE(state), NULL); active = g_private_get(state->active_group_key); if(active) { g_object_ref(active); } return (active); } /** * ipatch_state_record_item: * @state: State history object * @item: State item to add * * Add a state item to a state history object and advance the current position. */ void ipatch_state_record_item(IpatchState *state, IpatchStateItem *item) { IpatchStateGroup *active_group; GNode *n; g_return_if_fail(IPATCH_IS_STATE(state)); g_return_if_fail(item->node == NULL); g_return_if_fail(item->group == NULL); active_group = g_private_get(state->active_group_key); IPATCH_LOCK_WRITE(state); if(state->current_undo) /* called from an active undo? */ { /* recording redo state, use group of active undo item */ item->flags = IPATCH_STATE_ITEM_REDO | IPATCH_STATE_ITEM_ACTIVE; item->group = state->current_undo->group; n = g_node_prepend_data(state->redo_parent, item); state->redo_parent = n; /* in case there are multiple redo actions */ } else /* normal undo state recording */ { item->flags = IPATCH_STATE_ITEM_UNDO | IPATCH_STATE_ITEM_ACTIVE; item->group = active_group; n = g_node_prepend_data(state->position, item); /* add item to tree */ state->position = n; /* advance current position */ } item->node = n; IPATCH_UNLOCK_WRITE(state); g_object_ref(item); /* ++ ref item for item tree */ if(item->group) /* add the item to its group item list */ { g_object_ref(item->group); /* ++ ref group for item */ IPATCH_LOCK_WRITE(item->group); item->group->items = g_list_prepend(item->group->items, item); IPATCH_UNLOCK_WRITE(item->group); } } /** * ipatch_state_record: * @state: State history object * @type_name: Name of #IpatchStateItem derived type to record * @first_property_name: (nullable): Name of first item property to set or NULL to not * set any properties. * @Varargs: Name/value pairs of properties to set on new #IpatchStateItem item. * Variable argument list starts with the value for @first_property_name and * is terminated with a NULL name parameter. * * Records an action in a state history object. A state item of type * `@type_name' is created and property values are set to the passed * in values. The state item is then added to the state history object * and the current position is advanced. */ void ipatch_state_record(IpatchState *state, const char *type_name, const char *first_property_name, ...) { IpatchStateItem *item; va_list args; GType type; g_return_if_fail(IPATCH_IS_STATE(state)); type = g_type_from_name(type_name); g_return_if_fail(g_type_is_a(type, IPATCH_TYPE_STATE)); va_start(args, first_property_name); item = (IpatchStateItem *) /* ++ ref new item */ (g_object_new_valist(type, first_property_name, args)); va_end(args); ipatch_state_record_item(state, item); g_object_unref(item); /* -- unref creator's ref */ } /** * ipatch_state_retract: * @state: State history object * * Undos all actions in the current active sub group, flags the group as * retracted, and closes it. */ void ipatch_state_retract(IpatchState *state) { IpatchStateGroup *group; g_return_if_fail(IPATCH_IS_STATE(state)); group = g_private_get(state->active_group_key); if(!group) { g_critical(ERRMSG_NO_ACTIVE_STATE_GROUP); return; } IPATCH_LOCK_WRITE(group); ipatch_state_undo(state, group->items); group->flags |= IPATCH_STATE_GROUP_RETRACTED; IPATCH_UNLOCK_WRITE(group); } /** * ipatch_state_get_undo_depends: * @state: State history object * @items: List of #IpatchStateItem items to check undo dependencies of * * Get dependencies for a list of @items to undo. * * Returns: A list of #IpatchStateItem items that depend on @items. Only additional * dependencies are returned (not already in @items). %NULL is returned if * there are no more dependencies. List should be freed with * ipatch_state_list_free() when finished with it. */ #if 0 /* FIXME - Not finished */ GList * ipatch_state_get_undo_depends(IpatchState *state, const GList *items) { g_return_val_if_fail(IPATCH_IS_STATE(state), NULL); g_return_val_if_fail(items != NULL, NULL); } #endif /** * ipatch_state_undo: * @state: State history object * @items: (element-type IpatchStateItem): List of #IpatchStateItem items to undo * * Undo one or more items in a state history object. All active dependent * items are undone as well. All inactive affected items are migrated * appropriately. */ void ipatch_state_undo(IpatchState *state, GList *items) { IpatchStateItem *top_item; GList *dup_list, *p; GNode *n; g_return_if_fail(IPATCH_IS_STATE(state)); g_return_if_fail(items != NULL); /* optimized for small items list compared to history tree, we make a copy of the items list so we can remove them as they are found in the tree so we know when to stop */ dup_list = g_list_copy(items); IPATCH_LOCK_WRITE(state); /* locate the item closest to the root of the item tree */ n = state->position; top_item = NULL; while(n->data) { p = dup_list; while(p) /* see if this tree node matches one in 'items' */ { if(p->data == n->data) { top_item = p->data; /* set dependent flag */ top_item->flags |= IPATCH_STATE_ITEM_DEPENDENT; /* no more items in duplicate list? (all of them found) */ if(!(dup_list = g_list_delete_link(dup_list, p))) { goto outta_here; } break; /* next tree parent node */ } p = g_list_next(p); } n = n->parent; } g_list_free(dup_list); /* in case some items weren't found */ outta_here: if(!top_item) /* no items to undo?! */ { IPATCH_UNLOCK_WRITE(state); return; } /* recursively flag all items dependent on undo 'items' */ g_node_traverse(top_item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)traverse_set_depends, NULL); /* if we are undoing the current position, move position to first parent that isn't being un-done */ n = state->position; while(n) { if(!n->data || !(((IpatchStateItem *)(n->data))->flags & IPATCH_STATE_ITEM_DEPENDENT)) { state->position = n; break; } n = n->parent; } /* undo the dependent items, re-structure tree, etc */ traverse_undo(top_item, IPATCH_STATE_ITEM(top_item->node->parent->data), IPATCH_STATE_ITEM(top_item->node->parent->data), state); /* clear the dependent flag, unfortunately couldn't figure out a way to do this in traverse_undo */ g_node_traverse(top_item->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)traverse_unset_depends, NULL); IPATCH_UNLOCK_WRITE(state); return; } /* function that traverses over an item tree and marks items that are dependent on already dependent marked items */ static gboolean traverse_set_depends(GNode *node, gpointer data) { IpatchStateItem *item = (IpatchStateItem *)(node->data); if(item->flags & IPATCH_STATE_ITEM_DEPENDENT) g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc)traverse_is_dependent, item); return (FALSE); } /* traversal function to mark dependencies to an item passed as the 'data' parameter */ static gboolean traverse_is_dependent(GNode *node, gpointer data) { IpatchStateItem *item = (IpatchStateItem *)(node->data); IpatchStateItem *dep = (IpatchStateItem *)data; /* item is not already marked? (Has the added effect of stopping dependency checking of an item with itself). */ if(!(item->flags & IPATCH_STATE_ITEM_DEPENDENT) && ipatch_state_item_depend(item, dep)) /* item is dependent? */ { item->flags |= IPATCH_STATE_ITEM_DEPENDENT; } return (FALSE); } /* traversal function to unset dependent flag of all items */ static gboolean traverse_unset_depends(GNode *node, gpointer data) { IpatchStateItem *item = (IpatchStateItem *)(node->data); item->flags &= ~IPATCH_STATE_ITEM_DEPENDENT; return (FALSE); } /* traverses an item tree searching for item's flagged as dependent, these items are moved to their closest dependent parent and undo items are undone */ static void traverse_undo(IpatchStateItem *item, IpatchStateItem *last_depend, IpatchStateItem *last_nondep, IpatchState *state) { GNode *n; /* keep local vars to minimum since recursive func */ n = item->node->children; while(n) { register GNode *temp = n; /* register, so no stack is used */ n = n->next; /* must advance before call, since n might get removed */ traverse_undo((IpatchStateItem *)(temp->data), (item->flags & IPATCH_STATE_ITEM_DEPENDENT) ? item : last_depend, (item->flags & IPATCH_STATE_ITEM_DEPENDENT) ? last_nondep : item, state); } /* return if this item is not dependent */ if(!(item->flags & IPATCH_STATE_ITEM_DEPENDENT)) { return; } /* undo item? */ if((item->flags & IPATCH_STATE_ITEM_TYPE_MASK) == IPATCH_STATE_ITEM_UNDO) { /* Set restore vars to catch redo activity in ipatch_state_record_item(). Ref not required because we are locked for duration of use. */ state->current_undo = item; state->redo_parent = last_depend->node; /* parent of new redo state */ ipatch_state_item_restore(item); /* undo the action */ state->current_undo = NULL; /* state->redo_parent now contains last recorded redo state item */ /* move the old item's children to appropriate parent */ n = g_node_last_child(item->node); while(n) { GNode *n2 = n; n = n->prev; g_node_unlink(n2); if(((IpatchStateItem *)(n->data))->flags & IPATCH_STATE_ITEM_DEPENDENT) { g_node_prepend(state->redo_parent, n2); } else { g_node_prepend(last_nondep->node, n2); } } g_node_unlink(item->node); /* unlink old item node */ g_object_unref(item); /* -- unref the item (no longer in tree) */ } else /* dependent redo item */ { /* move to last non-dependent node */ if(last_nondep->node != item->node->parent) { g_node_unlink(item->node); g_node_prepend(last_nondep->node, item->node); } } } #if 0 /** * ipatch_state_redo: * @state: Ipatch state history object * @items: List of #IpatchStateItem items to redo * * Redo one or more items in a state history object. All dependent items are * also re-done. */ void ipatch_state_redo(IpatchState *state, GList *items) { IpatchStateItem *top_item; IpatchStateItemClass *item_class; GList *dup_list = NULL, *p; GNode *n; g_return_val_if_fail(IPATCH_IS_STATE(state), FALSE); g_return_val_if_fail(IPATCH_IS_STATE_ITEM(item), FALSE); g_return_val_if_fail(item->node != NULL, FALSE); item_class = IPATCH_STATE_ITEM_GET_CLASS(item); g_return_val_if_fail(item_class->restore != NULL, FALSE); g_return_val_if_fail(item_class->reverse != NULL, FALSE); IPATCH_LOCK_WRITE(state); dup_list = g_list_copy(items); /* flag all redo items, optimized for large state history, we climb up the undo state branch traversing its redo state children */ n = state->position; while(n->data) { traverse_redo_flag_items(n, &dup_list); if(!dup_list) { break; } n = n->parent; } g_list_free(dup_list); /* free remaining un-located items (if any) */ top_item = n; /* top most item encompassing all redo items */ IPATCH_UNLOCK_WRITE(state); } static void traverse_redo_flag_items(GNode *node, GList **items) { register IpatchStateItem *item = (IpatchStateItem *)(node->data); GNode *n; /* if node is a redo item and not yet flagged, see if its in items */ if(item->flags & IPATCH_STATE_ITEM_REDO && !(item->flags & IPATCH_STATE_ITEM_DEPENDENT)) { p = *items; while(p) { if(p->data == (void *)item) { *items = g_list_delete_link(*items, p); /* remove from items */ item->flags |= IPATCH_STATE_ITEM_DEPENDENT; /* flag it */ break; } p = g_list_next(p); } } if(!*items) { return; } n = node->children; while(n) { if(((IpatchStateItem *)(n->data))->flags & IPATCH_STATE_ITEM_REDO) { traverse_redo_flag_items(n, items); } n = n->next; } } #endif libinstpatch-1.1.6/libinstpatch/IpatchState.h000066400000000000000000000061671400263525300212630ustar00rootroot00000000000000/* * IpatchState.h - Header for state history system (undo/redo) * * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_STATE_H__ #define __IPATCH_STATE_H__ #include #include #include #include #include typedef struct _IpatchState IpatchState; typedef struct _IpatchStateClass IpatchStateClass; #define IPATCH_TYPE_STATE (ipatch_state_get_type ()) #define IPATCH_STATE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_STATE, IpatchState)) #define IPATCH_STATE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_STATE, IpatchStateClass)) #define IPATCH_IS_STATE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_STATE)) #define IPATCH_IS_STATE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_STATE)) #define IPATCH_STATE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_STATE, IpatchStateClass)) /* Ipatch State history object */ struct _IpatchState { IpatchLock parent_instance; /* derived from IpatchLock */ GNode *root; /* state tree (IpatchState) */ GNode *position; /* current position in state tree */ GNode *group_root; /* group tree (IpatchStateGroup) */ GPrivate *active_group_key; /* per thread active group (IpatchStateGroup)*/ IpatchStateItem *current_undo; /* current undo item or NULL */ GNode *redo_parent; /* current redo state parent (IpatchState GNode) */ }; /* Ipatch State history class */ struct _IpatchStateClass { IpatchLockClass parent_class; }; GType ipatch_state_get_type(void); IpatchState *ipatch_state_new(void); void ipatch_state_begin_group(IpatchState *state, const char *descr); void ipatch_state_end_group(IpatchState *state); void ipatch_state_set_active_group(IpatchState *state, IpatchStateGroup *group); IpatchStateGroup *ipatch_state_get_active_group(IpatchState *state); void ipatch_state_record_item(IpatchState *state, IpatchStateItem *item); void ipatch_state_record(IpatchState *state, const char *type_name, const char *first_property_name, ...); void ipatch_state_retract(IpatchState *state); /* GList *ipatch_state_get_undo_depends (IpatchState *state, const GList *items); */ void ipatch_state_undo(IpatchState *state, GList *items); void ipatch_state_redo(IpatchState *state, GList *items); #endif libinstpatch-1.1.6/libinstpatch/IpatchStateGroup.c000066400000000000000000000044311400263525300222630ustar00rootroot00000000000000/* * IpatchStateGroup.c - State (undo/redo) group object * * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #include #include #include #include "IpatchStateGroup.h" #include "ipatch_priv.h" static void ipatch_state_group_class_init(IpatchStateGroupClass *klass); static void ipatch_state_group_finalize(GObject *gobject); gpointer group_parent_class = NULL; GType ipatch_state_group_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchStateGroupClass), NULL, NULL, (GClassInitFunc) ipatch_state_group_class_init, NULL, NULL, sizeof(IpatchStateGroup), 0, (GInstanceInitFunc) NULL, }; item_type = g_type_register_static(IPATCH_TYPE_LOCK, "IpatchStateGroup", &item_info, 0); } return (item_type); } static void ipatch_state_group_class_init(IpatchStateGroupClass *klass) { GObjectClass *gobj_class = G_OBJECT_CLASS(klass); group_parent_class = g_type_class_peek_parent(klass); gobj_class->finalize = ipatch_state_group_finalize; } static void ipatch_state_group_finalize(GObject *gobject) { IpatchStateGroup *group = (IpatchStateGroup *)gobject; if(group->node) { g_node_destroy(group->node); /* destroy group's tree node */ } if(G_OBJECT_CLASS(group_parent_class)->finalize) { G_OBJECT_CLASS(group_parent_class)->finalize(gobject); } } libinstpatch-1.1.6/libinstpatch/IpatchStateGroup.h000066400000000000000000000047021400263525300222710ustar00rootroot00000000000000/* * IpatchStateGroup.h - State (undo/redo) group object * * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_STATE_GROUP_H__ #define __IPATCH_STATE_GROUP_H__ #include #include #include typedef struct _IpatchStateGroup IpatchStateGroup; typedef struct _IpatchStateGroupClass IpatchStateGroupClass; #define IPATCH_TYPE_STATE_GROUP (ipatch_state_group_get_type ()) #define IPATCH_STATE_GROUP(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_STATE_GROUP, IpatchStateGroup)) #define IPATCH_STATE_GROUP_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_STATE_GROUP, \ IpatchStateGroupClass)) #define IPATCH_IS_STATE_GROUP(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_STATE_GROUP)) #define IPATCH_IS_STATE_GROUP_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_STATE_GROUP)) #define IPATCH_STATE_GROUP_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_STATE_GROUP, \ IpatchStateGroupClass)) /* state group structure */ struct _IpatchStateGroup { IpatchLock parent_instance; /* derived from IpatchLock */ guint flags; /* group flags */ GNode *node; /* node in the group tree or NULL */ char *descr; /* description of this action, or NULL */ GList *items; /* list of IpatchStateItem objects (prepend) */ }; typedef enum { IPATCH_STATE_GROUP_RETRACTED = 1 << 0, /* group has been retracted */ IPATCH_STATE_GROUP_PARTIAL = 1 << 1 /* some item's missing from group */ } IpatchStateGroupFlags; /* state group class */ struct _IpatchStateGroupClass { IpatchLockClass parent_class; }; GType ipatch_state_group_get_type(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchStateItem.c000066400000000000000000000110001400263525300220530ustar00rootroot00000000000000/* * IpatchStateItem.c - Base class for state (undo/redo) items * * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #include #include #include #include "IpatchStateItem.h" static void ipatch_state_item_class_init(IpatchStateItemClass *klass); static void ipatch_state_item_finalize(GObject *gobject); gpointer item_parent_class = NULL; GType ipatch_state_item_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchStateItemClass), NULL, NULL, (GClassInitFunc) ipatch_state_item_class_init, NULL, NULL, sizeof(IpatchStateItem), 0, (GInstanceInitFunc) NULL, }; item_type = g_type_register_static(G_TYPE_OBJECT, "IpatchStateItem", &item_info, G_TYPE_FLAG_ABSTRACT); } return (item_type); } static void ipatch_state_item_class_init(IpatchStateItemClass *klass) { GObjectClass *gobj_class = G_OBJECT_CLASS(klass); item_parent_class = g_type_class_peek_parent(klass); gobj_class->finalize = ipatch_state_item_finalize; } static void ipatch_state_item_finalize(GObject *gobject) { IpatchStateItem *item = IPATCH_STATE_ITEM(gobject); if(item->node) { g_node_destroy(item->node); /* destroy item's tree node */ } if(item->group) { g_object_unref(item->group); /* -- unref item's group */ } if(G_OBJECT_CLASS(item_parent_class)->finalize) { G_OBJECT_CLASS(item_parent_class)->finalize(gobject); } } /** * ipatch_state_item_restore: * @item: State item to restore * * Restore the state saved by @item. */ void ipatch_state_item_restore(const IpatchStateItem *item) { IpatchStateItemClass *klass; g_return_if_fail(IPATCH_IS_STATE_ITEM(item)); klass = IPATCH_STATE_ITEM_GET_CLASS(item); g_return_if_fail(klass->restore != NULL); (*klass->restore)(item); } /** * ipatch_state_item_depend: * @item1: First item * @item2: Second item * * Check if @item1 is dependent on @item2. * * Returns: %TRUE if @item1 is dependent on @item2, %FALSE otherwise. */ gboolean ipatch_state_item_depend(const IpatchStateItem *item1, const IpatchStateItem *item2) { IpatchStateItemClass *klass; g_return_val_if_fail(IPATCH_IS_STATE_ITEM(item1), FALSE); g_return_val_if_fail(IPATCH_IS_STATE_ITEM(item2), FALSE); klass = IPATCH_STATE_ITEM_GET_CLASS(item1); return (!klass->depend || (*klass->depend)(item1, item2)); } /** * ipatch_state_item_conflict: * @item1: First item * @item2: Second item * * Check if @item1 conflicts with @item2. * * Returns: %TRUE if @item1 conflicts with @item2, %FALSE otherwise. */ gboolean ipatch_state_item_conflict(const IpatchStateItem *item1, const IpatchStateItem *item2) { IpatchStateItemClass *klass; g_return_val_if_fail(IPATCH_IS_STATE_ITEM(item1), FALSE); g_return_val_if_fail(IPATCH_IS_STATE_ITEM(item2), FALSE); klass = IPATCH_STATE_ITEM_GET_CLASS(item1); return (!klass->conflict || (*klass->conflict)(item1, item2)); } /** * ipatch_state_item_describe: * @item: Item to describe * * Get a detailed description of the action that created the @item state. * * Returns: Newly allocated description of the @item action or %NULL if * no description available. String should be freed when no longer needed. */ char * ipatch_state_item_describe(const IpatchStateItem *item) { IpatchStateItemClass *klass; g_return_val_if_fail(IPATCH_IS_STATE_ITEM(item), NULL); klass = IPATCH_STATE_ITEM_GET_CLASS(item); if(klass->describe) { return ((*klass->describe)(item)); } else { return (NULL); } } libinstpatch-1.1.6/libinstpatch/IpatchStateItem.h000066400000000000000000000071421400263525300220740ustar00rootroot00000000000000/* * IpatchStateItem.h - Base class for state (undo/redo) items * * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_STATE_ITEM_H__ #define __IPATCH_STATE_ITEM_H__ #include #include typedef struct _IpatchStateItem IpatchStateItem; typedef struct _IpatchStateItemClass IpatchStateItemClass; #include #include #define IPATCH_TYPE_STATE_ITEM (ipatch_state_item_get_type ()) #define IPATCH_STATE_ITEM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_STATE_ITEM, IpatchStateItem)) #define IPATCH_STATE_ITEM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_STATE_ITEM, \ IpatchStateItemClass)) #define IPATCH_IS_STATE_ITEM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_STATE_ITEM)) #define IPATCH_IS_STATE_ITEM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_STATE_ITEM)) #define IPATCH_STATE_ITEM_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_STATE_ITEM, \ IpatchStateItemClass)) /* state item */ struct _IpatchStateItem { GObject parent_instance; /* derived from GObject */ guint flags; /* flags for state items */ GNode *node; /* node in item tree or NULL */ IpatchStateGroup *group; /* group this item belongs to */ }; typedef enum { IPATCH_STATE_ITEM_UNDO = 0, /* state item holds undo data */ IPATCH_STATE_ITEM_REDO = 1 /* state item holds redo data */ } IpatchStateItemType; typedef enum { IPATCH_STATE_ITEM_TYPE_MASK = 1 << 0, /* mask for IpatchStateItemType */ IPATCH_STATE_ITEM_ACTIVE = 1 << 1, /* a flag for item's in item tree */ IPATCH_STATE_ITEM_DEPENDENT = 1 << 2 /* internal - to mark dependent items */ } IpatchStateItemFlags; /* reserve 5 bits for future use */ #define IPATCH_STATE_ITEM_UNUSED_FLAG_SHIFT 8 /* state item class */ struct _IpatchStateItemClass { GObjectClass parent_class; /* derived from GObject */ char *descr; /* text description of this state class */ /* methods */ void (*restore)(const IpatchStateItem *item); /* restore state */ gboolean(*depend)(const IpatchStateItem *item1, /* check dependency */ const IpatchStateItem *item2); gboolean(*conflict)(const IpatchStateItem *item1, /* check for conflicts */ const IpatchStateItem *item2); char *(*describe)(const IpatchStateItem *item); /* detailed description */ }; GType ipatch_state_item_get_type(void); void ipatch_state_item_restore(const IpatchStateItem *item); gboolean ipatch_state_item_depend(const IpatchStateItem *item1, const IpatchStateItem *item2); gboolean ipatch_state_item_conflict(const IpatchStateItem *item1, const IpatchStateItem *item2); char *ipatch_state_item_describe(const IpatchStateItem *item); #endif libinstpatch-1.1.6/libinstpatch/IpatchState_types.c000066400000000000000000000442421400263525300224760ustar00rootroot00000000000000/* * IpatchState_types.c - Builtin IpatchState objects * * Ipatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #include #include #include #include "IpatchState_types.h" #include "ipatch_priv.h" #include "i18n.h" enum { PROP_ITEM_ADD_0, PROP_ITEM_ADD_ITEM }; enum { PROP_ITEM_REMOVE_0, PROP_ITEM_REMOVE_ITEM, PROP_ITEM_REMOVE_PARENT }; enum { PROP_ITEM_CHANGE_0, PROP_ITEM_CHANGE_ITEM, PROP_ITEM_CHANGE_PSPEC, PROP_ITEM_CHANGE_VALUE }; static void item_add_class_init(IpatchStateItemAddClass *klass); static void item_add_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void item_add_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void item_add_finalize(GObject *object); static void item_add_restore(const IpatchStateItem *state); static void item_remove_class_init(IpatchStateItemRemoveClass *klass); static void item_remove_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void item_remove_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void item_remove_finalize(GObject *object); static void item_remove_restore(const IpatchStateItem *state); static gboolean item_remove_depend(const IpatchStateItem *item1, const IpatchStateItem *item2); static gboolean item_remove_conflict(const IpatchStateItem *item1, const IpatchStateItem *item2); static void item_change_class_init(IpatchStateItemChangeClass *klass); static void item_change_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void item_change_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void item_change_finalize(GObject *object); static void item_change_restore(const IpatchStateItem *state); static gboolean item_change_depend(const IpatchStateItem *item1, const IpatchStateItem *item2); static gboolean item_change_conflict(const IpatchStateItem *item1, const IpatchStateItem *item2); GObjectClass *item_add_parent_class = NULL; GObjectClass *item_remove_parent_class = NULL; GObjectClass *item_change_parent_class = NULL; /** * _ipatch_state_types_init: (skip) * * Function to initialize types */ void _ipatch_state_types_init(void) { g_type_class_ref(IPATCH_TYPE_STATE_ITEM_ADD); g_type_class_ref(IPATCH_TYPE_STATE_ITEM_REMOVE); g_type_class_ref(IPATCH_TYPE_STATE_ITEM_CHANGE); } /* -------------------------- */ /* patch item add state type */ /* -------------------------- */ GType ipatch_state_item_add_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchStateItemAddClass), NULL, NULL, (GClassInitFunc) item_add_class_init, NULL, NULL, sizeof(IpatchStateItemAdd), 0, (GInstanceInitFunc) NULL, }; item_type = g_type_register_static(IPATCH_TYPE_STATE_ITEM, "IpatchStateItemAdd", &item_info, 0); } return (item_type); } static void item_add_class_init(IpatchStateItemAddClass *klass) { IpatchStateItemClass *state_class = (IpatchStateItemClass *)klass; GObjectClass *gobj_class = (GObjectClass *)klass; item_add_parent_class = g_type_class_peek_parent(klass); gobj_class->set_property = item_add_set_property; gobj_class->get_property = item_add_get_property; gobj_class->finalize = item_add_finalize; state_class->restore = item_add_restore; state_class->depend = NULL; state_class->conflict = NULL; g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_ITEM_ADD_ITEM, g_param_spec_object("item", "Item", "Item that was added", IPATCH_TYPE_ITEM, G_PARAM_READWRITE)); } static void item_add_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchStateItemAdd *state; state = IPATCH_STATE_ITEM_ADD(object); switch(property_id) { case PROP_ITEM_ADD_ITEM: g_return_if_fail(state->item == NULL); state->item = g_value_get_object(value); g_return_if_fail(IPATCH_IS_ITEM(state->item)); g_object_ref(state->item); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void item_add_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchStateItemAdd *state; state = IPATCH_STATE_ITEM_ADD(object); switch(property_id) { case PROP_ITEM_ADD_ITEM: g_value_set_object(value, state->item); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void item_add_finalize(GObject *object) { IpatchStateItemAdd *state = IPATCH_STATE_ITEM_ADD(object); if(state->item) { g_object_unref(state->item); state->item = NULL; /* to catch busted refs to state object */ } G_OBJECT_CLASS(item_add_parent_class)->finalize(object); } static void item_add_restore(const IpatchStateItem *state) { IpatchStateItemAdd *item_add = IPATCH_STATE_ITEM_ADD(state); /* remove the item (the original state) */ ipatch_item_remove(item_add->item); } /* ---------------------------- */ /* patch item remove state type */ /* ---------------------------- */ GType ipatch_state_item_remove_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchStateItemRemoveClass), NULL, NULL, (GClassInitFunc) item_remove_class_init, NULL, NULL, sizeof(IpatchStateItemRemove), 0, (GInstanceInitFunc) NULL, }; item_type = g_type_register_static(IPATCH_TYPE_STATE_ITEM, "IpatchStateItemRemove", &item_info, 0); } return (item_type); } static void item_remove_class_init(IpatchStateItemRemoveClass *klass) { IpatchStateItemClass *state_class = (IpatchStateItemClass *)klass; GObjectClass *gobj_class = (GObjectClass *)klass; item_remove_parent_class = g_type_class_peek_parent(klass); gobj_class->set_property = item_remove_set_property; gobj_class->get_property = item_remove_get_property; gobj_class->finalize = item_remove_finalize; state_class->restore = item_remove_restore; state_class->depend = item_remove_depend; state_class->conflict = item_remove_conflict; g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_ITEM_REMOVE_ITEM, g_param_spec_object("item", "Item", "Item that will be removed", IPATCH_TYPE_ITEM, G_PARAM_READWRITE)); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_ITEM_REMOVE_PARENT, g_param_spec_object("parent", "Parent", "Parent of item to be removed", IPATCH_TYPE_ITEM, G_PARAM_READWRITE)); } static void item_remove_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchStateItemRemove *state; state = IPATCH_STATE_ITEM_REMOVE(object); switch(property_id) { case PROP_ITEM_REMOVE_ITEM: g_return_if_fail(state->item == NULL); state->item = g_value_get_object(value); g_return_if_fail(IPATCH_IS_ITEM(state->item)); g_object_ref(state->item); if(!state->parent) { state->parent = ipatch_item_get_parent(state->item); } break; case PROP_ITEM_REMOVE_PARENT: if(state->parent) { g_object_unref(state->parent); } state->parent = g_value_get_object(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void item_remove_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchStateItemRemove *state; state = IPATCH_STATE_ITEM_REMOVE(object); switch(property_id) { case PROP_ITEM_REMOVE_ITEM: g_value_set_object(value, state->item); break; case PROP_ITEM_REMOVE_PARENT: g_value_set_object(value, state->parent); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void item_remove_finalize(GObject *object) { IpatchStateItemRemove *state = IPATCH_STATE_ITEM_REMOVE(object); if(state->item) { g_object_unref(state->item); } if(state->parent) { g_object_unref(state->parent); } state->item = NULL; /* to catch busted refs to state object */ state->parent = NULL; G_OBJECT_CLASS(item_remove_parent_class)->finalize(object); } static void item_remove_restore(const IpatchStateItem *state) { IpatchStateItemRemove *item_remove; g_return_if_fail(IPATCH_IS_STATE_ITEM_REMOVE(state)); item_remove = IPATCH_STATE_ITEM_REMOVE(state); /* add the item (the original state) FIXME - Attempt to return item to its original position */ ipatch_container_append(IPATCH_CONTAINER(item_remove->parent), IPATCH_ITEM(item_remove->item)); } static gboolean item_remove_depend(const IpatchStateItem *item1, const IpatchStateItem *item2) { IpatchStateItemRemove *state; g_return_val_if_fail(IPATCH_IS_STATE_ITEM_REMOVE(item1), FALSE); state = IPATCH_STATE_ITEM_REMOVE(item1); /* "remove" depends on "add" with the same item */ return (IPATCH_IS_STATE_ITEM_ADD(item2) && IPATCH_STATE_ITEM_ADD(item2)->item == state->item); } static gboolean item_remove_conflict(const IpatchStateItem *item1, const IpatchStateItem *item2) { IpatchStateItemRemove *state; g_return_val_if_fail(IPATCH_IS_STATE_ITEM_REMOVE(item1), FALSE); state = IPATCH_STATE_ITEM_REMOVE(item1); /* "remove" conflicts with "remove" with the same item */ return (IPATCH_IS_STATE_ITEM_REMOVE(item2) && IPATCH_STATE_ITEM_REMOVE(item2)->item == state->item); } /* ------------------------------------- */ /* patch item property change state type */ /* ------------------------------------- */ GType ipatch_state_item_change_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchStateItemChangeClass), NULL, NULL, (GClassInitFunc) item_change_class_init, NULL, NULL, sizeof(IpatchStateItemChange), 0, (GInstanceInitFunc) NULL, }; item_type = g_type_register_static(IPATCH_TYPE_STATE_ITEM, "IpatchStateItemChange", &item_info, 0); } return (item_type); } static void item_change_class_init(IpatchStateItemChangeClass *klass) { IpatchStateItemClass *state_class = (IpatchStateItemClass *)klass; GObjectClass *gobj_class = (GObjectClass *)klass; item_change_parent_class = g_type_class_peek_parent(klass); gobj_class->set_property = item_change_set_property; gobj_class->get_property = item_change_get_property; gobj_class->finalize = item_change_finalize; state_class->restore = item_change_restore; state_class->depend = item_change_depend; state_class->conflict = item_change_conflict; g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_ITEM_CHANGE_ITEM, g_param_spec_object("item", "Item", "Item that has changed", IPATCH_TYPE_ITEM, G_PARAM_READWRITE)); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_ITEM_CHANGE_PSPEC, g_param_spec_pointer("pspec", "GParamSpec", "GParamSpec of the changed property", G_PARAM_READWRITE)); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_ITEM_CHANGE_VALUE, g_param_spec_boxed("value", "Value", "Old value of changed property", G_TYPE_VALUE, G_PARAM_READWRITE)); } static void item_change_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchStateItemChange *state = IPATCH_STATE_ITEM_CHANGE(object); GParamSpec *p; GValue *invalue; switch(property_id) { case PROP_ITEM_CHANGE_ITEM: g_return_if_fail(state->item == NULL); state->item = g_value_get_object(value); g_return_if_fail(IPATCH_IS_ITEM(state->item)); g_object_ref(state->item); break; case PROP_ITEM_CHANGE_PSPEC: p = g_value_get_pointer(value); g_return_if_fail(G_IS_PARAM_SPEC(p)); if(state->pspec) { g_param_spec_unref(state->pspec); /* -- unref old */ } g_param_spec_ref(p); /* ++ ref param spec */ state->pspec = p; break; case PROP_ITEM_CHANGE_VALUE: invalue = g_value_get_boxed(value); g_return_if_fail(G_IS_VALUE(invalue)); if(G_IS_VALUE(&state->value)) { g_value_unset(&state->value); } g_value_init(&state->value, G_VALUE_TYPE(invalue)); g_value_copy(invalue, &state->value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void item_change_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchStateItemChange *state; state = IPATCH_STATE_ITEM_CHANGE(object); switch(property_id) { case PROP_ITEM_CHANGE_ITEM: g_value_set_object(value, state->item); break; case PROP_ITEM_CHANGE_PSPEC: g_value_set_pointer(value, state->pspec); break; case PROP_ITEM_CHANGE_VALUE: g_value_set_static_boxed(value, &state->value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void item_change_finalize(GObject *object) { IpatchStateItemChange *state = IPATCH_STATE_ITEM_CHANGE(object); if(state->item) { g_object_unref(state->item); state->item = NULL; /* to catch busted refs to state object */ } if(state->pspec) /* unref param spec (if any) */ { g_param_spec_unref(state->pspec); state->pspec = NULL; } g_value_unset(&state->value); G_OBJECT_CLASS(item_change_parent_class)->finalize(object); } static void item_change_restore(const IpatchStateItem *state) { IpatchStateItemChange *item_change; g_return_if_fail(IPATCH_IS_STATE_ITEM_CHANGE(state)); item_change = IPATCH_STATE_ITEM_CHANGE(state); /* OPTME: Could call item_class->set_property directly */ g_object_set_property(G_OBJECT(item_change->item), item_change->pspec->name, &item_change->value); } static gboolean item_change_depend(const IpatchStateItem *item1, const IpatchStateItem *item2) { IpatchStateItemChange *state; g_return_val_if_fail(IPATCH_IS_STATE_ITEM_CHANGE(item1), FALSE); state = IPATCH_STATE_ITEM_CHANGE(item1); /* "patch change" depends on "add" with the same item or "patch change" with same item and property */ return ((IPATCH_IS_STATE_ITEM_ADD(item2) && ((IpatchStateItemAdd *)(item2))->item == state->item) || (IPATCH_IS_STATE_ITEM_CHANGE(item2) && ((IpatchStateItemChange *)(item2))->item == state->item && ((IpatchStateItemChange *)(item2))->pspec == state->pspec)); } static gboolean item_change_conflict(const IpatchStateItem *item1, const IpatchStateItem *item2) { IpatchStateItemChange *state; g_return_val_if_fail(IPATCH_IS_STATE_ITEM_CHANGE(item1), FALSE); state = IPATCH_STATE_ITEM_CHANGE(item1); /* "patch change" conflicts with "patch change" with same item and property */ return (IPATCH_IS_STATE_ITEM_CHANGE(item2) && ((IpatchStateItemChange *)(item2))->item == state->item && ((IpatchStateItemChange *)(item2))->pspec == state->pspec); } libinstpatch-1.1.6/libinstpatch/IpatchState_types.h000066400000000000000000000116261400263525300225030ustar00rootroot00000000000000/* * IpatchState_types.h - Builtin IpatchState (undo/redo history) objects * * Ipatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_STATE_TYPES_H__ #define __IPATCH_STATE_TYPES_H__ #include typedef struct _IpatchStateItemAdd IpatchStateItemAdd; typedef struct _IpatchStateItemAddClass IpatchStateItemAddClass; typedef struct _IpatchStateItemRemove IpatchStateItemRemove; typedef struct _IpatchStateItemRemoveClass IpatchStateItemRemoveClass; typedef struct _IpatchStateItemChange IpatchStateItemChange; typedef struct _IpatchStateItemChangeClass IpatchStateItemChangeClass; #include #define IPATCH_TYPE_STATE_ITEM_ADD (ipatch_state_item_add_get_type ()) #define IPATCH_STATE_ITEM_ADD(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_STATE_ITEM_ADD, \ IpatchStateItemAdd)) #define IPATCH_STATE_ITEM_ADD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_STATE_ITEM_ADD, \ IpatchStateItemAddClass)) #define IPATCH_IS_STATE_ITEM_ADD(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_STATE_ITEM_ADD)) #define IPATCH_IS_STATE_ITEM_ADD_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_STATE_ITEM_ADD)) #define IPATCH_STATE_ITEM_ADD_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_STATE_ITEM_ADD, \ IpatchStateItemAddClass)) #define IPATCH_TYPE_STATE_ITEM_REMOVE (ipatch_state_item_remove_get_type ()) #define IPATCH_STATE_ITEM_REMOVE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_STATE_ITEM_REMOVE, \ IpatchStateItemRemove)) #define IPATCH_STATE_ITEM_REMOVE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_STATE_ITEM_REMOVE, \ IpatchStateItemRemoveClass)) #define IPATCH_IS_STATE_ITEM_REMOVE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_STATE_ITEM_REMOVE)) #define IPATCH_IS_STATE_ITEM_REMOVE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_STATE_ITEM_REMOVE)) #define IPATCH_STATE_ITEM_REMOVE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_STATE_ITEM_REMOVE, \ IpatchStateItemRemoveClass)) #define IPATCH_TYPE_STATE_ITEM_CHANGE (ipatch_state_item_change_get_type ()) #define IPATCH_STATE_ITEM_CHANGE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_STATE_ITEM_CHANGE, \ IpatchStateItemChange)) #define IPATCH_STATE_ITEM_CHANGE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_STATE_ITEM_CHANGE, \ IpatchStateItemChangeClass)) #define IPATCH_IS_STATE_ITEM_CHANGE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_STATE_ITEM_CHANGE)) #define IPATCH_IS_STATE_ITEM_CHANGE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_STATE_ITEM_CHANGE)) #define IPATCH_STATE_ITEM_CHANGE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_STATE_ITEM_CHANGE, \ IpatchStateItemChangeClass)) /* Add IpatchItem state object */ struct _IpatchStateItemAdd { IpatchStateItem parent_instance; /* derived from IpatchStateItem */ IpatchItem *item; /* item that was added */ }; /* Add IpatchItem state class */ struct _IpatchStateItemAddClass { IpatchStateItemClass parent_class; /* derived from IpatchStateItem */ }; /* Remove IpatchItem state object */ struct _IpatchStateItemRemove { IpatchStateItem parent_instance; /* derived from IpatchStateItem */ IpatchItem *item; /* item that was removed */ IpatchItem *parent; /* parent of item that was removed */ }; /* Remove IpatchItem state class */ struct _IpatchStateItemRemoveClass { IpatchStateItemClass parent_class; /* derived from IpatchStateItem */ }; /* IpatchItem property change state object */ struct _IpatchStateItemChange { IpatchStateItem parent_instance; /* derived from IpatchStateItem */ IpatchItem *item; /* item that has changed */ GParamSpec *pspec; /* parameter spec of changed property */ GValue value; /* old property value */ }; /* IpatchItem property change state class */ struct _IpatchStateItemChangeClass { IpatchStateItemClass parent_class; /* derived from IpatchStateItem */ }; GType ipatch_state_item_add_get_type(void); GType ipatch_state_item_remove_get_type(void); GType ipatch_state_item_change_get_type(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchTypeProp.c000066400000000000000000000743361400263525300217630ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchTypeProp * @short_description: GObject style properties for GTypes * @see_also: * @stability: Stable * * Provides a registry system for adding GObject style properties to GTypes. * This is used to describe certain properties of different objects, such as * "category". */ #include #include #include #include #include #include "IpatchTypeProp.h" #include "IpatchVirtualContainer.h" #include "builtin_enums.h" #include "ipatch_priv.h" #include "i18n.h" /* key used for hash of GType property values */ typedef struct { GType type; /* type this property is bound to */ GParamSpec *spec; /* the parameter spec of the property */ } TypePropValueKey; typedef struct { GValue value; /* a static assigned value (or NULL) */ IpatchTypePropGetFunc func; /* a dynamic prop function (or NULL) */ GDestroyNotify notify_func; gpointer user_data; } TypePropValueVal; static guint type_prop_value_GHashFunc(gconstpointer key); static gboolean type_prop_value_GEqualFunc(gconstpointer a, gconstpointer b); static void type_prop_value_destroy(gpointer user_data); static void type_list_properties_GHFunc(gpointer key, gpointer value, gpointer user_data); static void type_set_property(GType type, GParamSpec *prop_spec, const GValue *value, IpatchTypePropGetFunc func, GDestroyNotify notify_func, gpointer user_data); static void type_get_property(GType type, GParamSpec *prop_spec, GValue *value, GObject *object); G_LOCK_DEFINE_STATIC(type_prop_hash); G_LOCK_DEFINE_STATIC(type_prop_value_hash); /* GType GParamSpec property hash (PropNameQuark -> GParamSpec) */ static GHashTable *type_prop_hash = NULL; /* hash of all GType property values (GType:GParamSpec -> GValue) */ static GHashTable *type_prop_value_hash = NULL; /*----------------------------------------------------------------------------- Initialization/deinitialization of GType property system ----------------------------------------------------------------------------*/ /** * _ipatch_type_prop_init: (skip) * * Initialize GType property system */ void _ipatch_type_prop_init(void) { type_prop_hash = g_hash_table_new(NULL, NULL); type_prop_value_hash = g_hash_table_new_full(type_prop_value_GHashFunc, type_prop_value_GEqualFunc, (GDestroyNotify)g_free, (GDestroyNotify)type_prop_value_destroy); /* install some default type properties */ /* a user friendly type name */ ipatch_type_install_property (g_param_spec_string("name", "Name", "Name", NULL, G_PARAM_READWRITE)); /* title of the object (usually dynamically created from obj instance) */ ipatch_type_install_property (g_param_spec_string("title", "Title", "Title", NULL, G_PARAM_READWRITE)); /* a user friendly type detail name */ ipatch_type_install_property (g_param_spec_string("blurb", "Blurb", "Blurb", NULL, G_PARAM_READWRITE)); /* type classes (see ipatch_type_prop_register_category) */ ipatch_type_install_property (g_param_spec_int("category", "Category", "Type category", G_MININT, G_MAXINT, IPATCH_CATEGORY_NONE, G_PARAM_READWRITE)); /* virtual parent container type (defined for children of * IpatchVirtualContainer types) */ ipatch_type_install_property (g_param_spec_gtype("virtual-parent-type", "Virtual parent type", "Virtual parent type", G_TYPE_NONE, G_PARAM_READWRITE)); /* virtual container child type (defined for IpatchVirtualContainer types) */ ipatch_type_install_property (g_param_spec_gtype("virtual-child-type", "Virtual child type", "Virtual child type", G_TYPE_NONE, G_PARAM_READWRITE)); /* link item type (type of object referenced/linked by another) */ ipatch_type_install_property (g_param_spec_gtype("link-type", "Link type", "Link type", G_TYPE_NONE, G_PARAM_READWRITE)); /* virtual container conform function (function pointer used for making * a child object conform to the virtual container's criteria, the "percussion" * property for example) See IpatchVirtualContainerConformFunc. */ ipatch_type_install_property(g_param_spec_pointer ("virtual-child-conform-func", "IpatchVirtualContainerConformFunc", "IpatchVirtualContainerConformFunc", G_PARAM_READWRITE)); /* sort a container's children in user interfaces? */ ipatch_type_install_property (g_param_spec_boolean("sort-children", "Sort children", "Sort children", FALSE, G_PARAM_READWRITE)); /* splits type property (for note and velocity splits) */ ipatch_type_install_property (g_param_spec_enum("splits-type", "Splits type", "Splits type", IPATCH_TYPE_SPLITS_TYPE, IPATCH_SPLITS_NONE, G_PARAM_READWRITE)); /* mime type for IpatchFile derived types */ ipatch_type_install_property (g_param_spec_string("mime-type", "Mime type", "Mime type", NULL, G_PARAM_READWRITE)); } /* hash function for GType property value hash */ static guint type_prop_value_GHashFunc(gconstpointer key) { TypePropValueKey *valkey = (TypePropValueKey *)key; return ((guint)(valkey->type) + G_PARAM_SPEC_TYPE(valkey->spec)); } /* key equal function for GType property value hash */ static gboolean type_prop_value_GEqualFunc(gconstpointer a, gconstpointer b) { TypePropValueKey *akey = (TypePropValueKey *)a; TypePropValueKey *bkey = (TypePropValueKey *)b; return (akey->type == bkey->type && akey->spec == bkey->spec); } /* destroy notify for GType property values */ static void type_prop_value_destroy(gpointer user_data) { TypePropValueVal *val = (TypePropValueVal *)user_data; if(G_IS_VALUE(&val->value)) { g_value_unset(&val->value); } if(val->notify_func) { val->notify_func(val->user_data); } g_slice_free(TypePropValueVal, val); } /** * _ipatch_type_prop_deinit * * Free GType property system */ void _ipatch_type_prop_deinit (void) { g_hash_table_destroy(type_prop_hash); g_hash_table_destroy(type_prop_value_hash); } /** * ipatch_type_install_property: * @prop_spec: (transfer full): Specification for the new #GType property. Ownership * of the GParamSpec is taken over by this function. The name field of * the GParamSpec should be a static string and is used as the property's * ID. * * Install a new #GType property which can be used to associate arbitrary * information to GTypes. */ void ipatch_type_install_property(GParamSpec *prop_spec) { GQuark quark; g_return_if_fail(G_IS_PARAM_SPEC(prop_spec)); g_return_if_fail(prop_spec->name != NULL); /* take ownership of the parameter spec */ g_param_spec_ref(prop_spec); g_param_spec_sink(prop_spec); quark = g_quark_from_static_string(prop_spec->name); G_LOCK(type_prop_hash); g_hash_table_insert(type_prop_hash, GUINT_TO_POINTER(quark), prop_spec); G_UNLOCK(type_prop_hash); } /** * ipatch_type_find_property: * @name: Name of GType property * * Lookup a GType property by name. * * Returns: (transfer none): The matching GParamSpec or %NULL if not found. The GParamSpec is * internal and should NOT be modified or freed. */ GParamSpec * ipatch_type_find_property(const char *name) { GParamSpec *spec; GQuark quark; g_return_val_if_fail(name != NULL, NULL); quark = g_quark_try_string(name); if(!quark) { return (NULL); } G_LOCK(type_prop_hash); spec = g_hash_table_lookup(type_prop_hash, GUINT_TO_POINTER(quark)); G_UNLOCK(type_prop_hash); return (spec); } /** * ipatch_type_list_properties: * @n_properties: (out): Length of returned array * * Get a list of all registered GType properties. * * Returns: (array length=n_properties) (transfer container): An array of GParamSpecs * which should be freed when finished (only the array, not the GParamSpecs themselves) */ GParamSpec ** ipatch_type_list_properties(guint *n_properties) { GParamSpec **specs, **specp; g_return_val_if_fail(n_properties != NULL, NULL); G_LOCK(type_prop_hash); specs = g_new(GParamSpec *, g_hash_table_size(type_prop_hash)); specp = specs; g_hash_table_foreach(type_prop_hash, type_list_properties_GHFunc, &specp); G_UNLOCK(type_prop_hash); return (specs); } /* fills an array with GParamSpecs in the type_prop_hash */ static void type_list_properties_GHFunc(gpointer key, gpointer value, gpointer user_data) { GParamSpec ***specs = user_data; **specs = (GParamSpec *)value; *specs = *specs + 1; } /** * ipatch_type_find_types_with_property: * @name: Name of type property * @value: (nullable): Optional value to match to type property values * (%NULL to match any value) * @n_types: (out) (optional): Location to store count of types in returned * array or %NULL to ignore * * Get an array of types which have the given type property assigned and match * @value (optional, %NULL matches any value). * * Returns: (array length=n_types): Newly allocated 0 terminated array of types * which have the named property set or %NULL if type property not found. */ GType * ipatch_type_find_types_with_property(const char *name, const GValue *value, guint *n_types) { TypePropValueKey *key; GParamSpec *pspec; GList *keys, *p, *temp; GType *types; int count = 0; int i; g_return_val_if_fail(name != NULL, NULL); pspec = ipatch_type_find_property(name); g_return_val_if_fail(pspec != NULL, NULL); G_LOCK(type_prop_value_hash); keys = g_hash_table_get_keys(type_prop_value_hash); /* ++ alloc keys list */ /* Convert keys list to list of matching GTypes */ for(p = keys; p;) { key = p->data; if(key->spec == pspec) { /* Replace list data TypePropValueKey pointer to GType */ p->data = GSIZE_TO_POINTER(key->type); p = p->next; } else /* Doesn't match GParamSpec - remove from list */ { if(p->prev) { p->prev->next = p->next; } else { keys = p->next; } if(p->next) { p->next->prev = p->prev; } temp = p; p = p->next; g_list_free1(temp); } } G_UNLOCK(type_prop_value_hash); /* Compare values if @value was supplied */ if(value) { GValue cmp_value = { 0 }; GType type; for(p = keys; p;) { type = GPOINTER_TO_SIZE(p->data); g_value_init(&cmp_value, G_PARAM_SPEC_VALUE_TYPE(pspec)); ipatch_type_get_property(type, pspec->name, &cmp_value); if(g_param_values_cmp(pspec, value, &cmp_value) != 0) { if(p->prev) { p->prev->next = p->next; } else { keys = p->next; } if(p->next) { p->next->prev = p->prev; } temp = p; p = p->next; g_list_free1(temp); } else { p = p->next; } g_value_unset(&cmp_value); } } count = g_list_length(keys); types = g_new(GType, count + 1); /* ++ alloc types */ /* Copy GType list to type array and delete the list */ for(p = keys, i = 0; p; p = g_list_delete_link(p, p), i++) /* -- free keys list */ { types[i] = GPOINTER_TO_SIZE(p->data); } types[i] = 0; if(n_types) { *n_types = count; } return (types); /* !! caller takes over alloc */ } /** * ipatch_type_set: * @type: GType to set properties of * @first_property_name: Name of first property to set * @...: Value of first property to set and optionally followed by more * property name/value pairs, terminated with %NULL name. * * Set GType properties. GType properties are used to associate arbitrary * information with GTypes. */ void ipatch_type_set(GType type, const char *first_property_name, ...) { va_list args; va_start(args, first_property_name); ipatch_type_set_valist(type, first_property_name, args); va_end(args); } /** * ipatch_type_set_valist: * @type: GType to set properties of * @first_property_name: Name of first property to set * @args: Value of first property to set and optionally followed by more * property name/value pairs, terminated with %NULL name. * * Like ipatch_type_set() but uses a va_list. */ void ipatch_type_set_valist(GType type, const char *first_property_name, va_list args) { const char *name; GValue value = { 0 }; gchar *error = NULL; GParamSpec *prop_spec; g_return_if_fail(type != 0); g_return_if_fail(first_property_name != NULL); name = first_property_name; while(name) { prop_spec = ipatch_type_find_property(name); if(!prop_spec) { g_warning("%s: no type property named `%s'", G_STRLOC, name); break; } if(!(prop_spec->flags & G_PARAM_WRITABLE)) { g_warning("%s: type property `%s' is not writable", G_STRLOC, prop_spec->name); break; } g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(prop_spec)); G_VALUE_COLLECT(&value, args, 0, &error); if(error) { g_warning("%s: %s", G_STRLOC, error); g_free(error); /* we purposely leak the GValue contents here, it might not be * in a sane state if an error condition occured */ break; } type_set_property(type, prop_spec, &value, NULL, NULL, NULL); g_value_unset(&value); name = va_arg(args, char *); } } /** * ipatch_type_set_property: * @type: GType to set property of * @property_name: Name of property to set * @value: Value to set the the property to. The value's * type must be the same as the GType property's type. * * Set a single property of a #GType. */ void ipatch_type_set_property(GType type, const char *property_name, const GValue *value) { GParamSpec *prop_spec; g_return_if_fail(type != 0); g_return_if_fail(property_name != NULL); g_return_if_fail(G_IS_VALUE(value)); prop_spec = ipatch_type_find_property(property_name); if(!prop_spec) { g_warning("%s: no type property named `%s'", G_STRLOC, property_name); return; } if(!(prop_spec->flags & G_PARAM_WRITABLE)) { g_warning("%s: type property `%s' is not writable", G_STRLOC, property_name); return; } if(G_VALUE_TYPE(value) == G_PARAM_SPEC_VALUE_TYPE(prop_spec)) { g_warning("%s: value type should be '%s' but is '%s'", G_STRLOC, g_type_name(G_PARAM_SPEC_VALUE_TYPE(prop_spec)), G_VALUE_TYPE_NAME(value)); return; } type_set_property(type, prop_spec, value, NULL, NULL, NULL); } /* does the actual setting of a GType property, note that the value is is copied and not used directly */ static void type_set_property(GType type, GParamSpec *prop_spec, const GValue *value, IpatchTypePropGetFunc func, GDestroyNotify notify_func, gpointer user_data) { TypePropValueKey *key; TypePropValueVal *val; key = g_new(TypePropValueKey, 1); key->type = type; key->spec = prop_spec; val = g_slice_new0(TypePropValueVal); if(value) { g_value_init(&val->value, G_VALUE_TYPE(value)); g_value_copy(value, &val->value); } val->func = func; val->notify_func = notify_func; val->user_data = user_data; /* value is taken over by the hash table */ G_LOCK(type_prop_value_hash); g_hash_table_insert(type_prop_value_hash, key, val); G_UNLOCK(type_prop_value_hash); } /** * ipatch_type_unset_property: * @type: GType to unset a property of * @property_name: The property to unset * * Unsets the value or dynamic function of a type property. * * Since: 1.1.0 */ void ipatch_type_unset_property(GType type, const char *property_name) { GParamSpec *prop_spec; TypePropValueKey key; g_return_if_fail(type != 0); g_return_if_fail(property_name != NULL); prop_spec = ipatch_type_find_property(property_name); if(!prop_spec) { g_warning("%s: no type property named `%s'", G_STRLOC, property_name); return; } if(!(prop_spec->flags & G_PARAM_WRITABLE)) { g_warning("%s: type property `%s' is not writable", G_STRLOC, property_name); return; } key.type = type; key.spec = prop_spec; G_LOCK(type_prop_value_hash); g_hash_table_remove(type_prop_value_hash, &key); G_UNLOCK(type_prop_value_hash); } /** * ipatch_type_get: * @type: GType to get properties from * @first_property_name: Name of first property to get * @...: Pointer to store first property value and optionally followed * by more property name/value pairs, terminated with %NULL name. * * Get GType property values. */ void ipatch_type_get(GType type, const char *first_property_name, ...) { va_list args; va_start(args, first_property_name); ipatch_type_get_valist(type, first_property_name, args); va_end(args); } /** * ipatch_type_get_valist: * @type: GType to get properties from * @first_property_name: Name of first property to get * @args: Pointer to store first property value and optionally followed * by more property name/value pairs, terminated with %NULL name. * * Like ipatch_type_get() but uses a va_list. */ void ipatch_type_get_valist(GType type, const char *first_property_name, va_list args) { const char *name; g_return_if_fail(type != 0); g_return_if_fail(first_property_name != NULL); name = first_property_name; while(name) { GValue value = { 0, }; GParamSpec *prop_spec; char *error; prop_spec = ipatch_type_find_property(name); if(!prop_spec) { g_warning("%s: no type property named `%s'", G_STRLOC, name); break; } if(!(prop_spec->flags & G_PARAM_READABLE)) { g_warning("%s: type property `%s' is not readable", G_STRLOC, prop_spec->name); break; } g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(prop_spec)); type_get_property(type, prop_spec, &value, NULL); G_VALUE_LCOPY(&value, args, 0, &error); if(error) { g_warning("%s: %s", G_STRLOC, error); g_free(error); g_value_unset(&value); break; } g_value_unset(&value); name = va_arg(args, char *); } } /** * ipatch_type_get_property: * @type: GType to get property from * @property_name: Name of property to get * @value: (out): An initialized value to store the property value in. The value's * type must be a type that the property can be transformed to. * * Get a single property from a #GType. */ void ipatch_type_get_property(GType type, const char *property_name, GValue *value) { GParamSpec *prop_spec; g_return_if_fail(type != 0); g_return_if_fail(property_name != NULL); g_return_if_fail(G_IS_VALUE(value)); prop_spec = ipatch_type_find_property(property_name); if(!prop_spec) g_warning("%s: no type property named `%s'", G_STRLOC, property_name); else if(!(prop_spec->flags & G_PARAM_READABLE)) g_warning("%s: type property `%s' is not readable", G_STRLOC, prop_spec->name); else { GValue *prop_value, tmp_value = { 0, }; /* auto-conversion of the callers value type */ if(G_VALUE_TYPE(value) == G_PARAM_SPEC_VALUE_TYPE(prop_spec)) { g_value_reset(value); prop_value = value; } else if(!g_value_type_transformable(G_PARAM_SPEC_VALUE_TYPE(prop_spec), G_VALUE_TYPE(value))) { g_warning("can't retrieve type property `%s' of type" " `%s' as value of type `%s'", prop_spec->name, g_type_name(G_PARAM_SPEC_VALUE_TYPE(prop_spec)), G_VALUE_TYPE_NAME(value)); return; } else { g_value_init(&tmp_value, G_PARAM_SPEC_VALUE_TYPE(prop_spec)); prop_value = &tmp_value; } type_get_property(type, prop_spec, prop_value, NULL); if(prop_value != value) { g_value_transform(prop_value, value); g_value_unset(&tmp_value); } } } /* does the actual getting of a GType property */ static void type_get_property(GType type, GParamSpec *prop_spec, GValue *value, GObject *object) { TypePropValueKey key; TypePropValueVal *val; key.type = type; key.spec = prop_spec; G_LOCK(type_prop_value_hash); val = g_hash_table_lookup(type_prop_value_hash, &key); G_UNLOCK(type_prop_value_hash); if(val) { if(val->func) { val->func(type, prop_spec, value, object); } else { g_value_copy(&val->value, value); } } else { g_param_value_set_default(prop_spec, value); } } /** * ipatch_type_object_get: * @object: Object instance to get type property from * @first_property_name: Name of first property to get * @...: Pointer to store first property value and optionally followed * by more property name/value pairs, terminated with %NULL name. * * Get GType property values. Like ipatch_type_get() but takes an object * instance which can be used by any registered dynamic type property * functions. */ void ipatch_type_object_get(GObject *object, const char *first_property_name, ...) { va_list args; va_start(args, first_property_name); ipatch_type_object_get_valist(object, first_property_name, args); va_end(args); } /* * ipatch_type_object_get_valist: * @object: Object instance to get type property from * @first_property_name: Name of first property to get * @args: Pointer to store first property value and optionally followed * by more property name/value pairs, terminated with %NULL name. * * Like ipatch_type_object_get() but uses a va_list. */ void ipatch_type_object_get_valist(GObject *object, const char *first_property_name, va_list args) { GType type; const char *name; g_return_if_fail(G_IS_OBJECT(object)); g_return_if_fail(first_property_name != NULL); type = G_OBJECT_TYPE(object); g_return_if_fail(type != 0); name = first_property_name; while(name) { GValue value = { 0, }; GParamSpec *prop_spec; char *error; prop_spec = ipatch_type_find_property(name); if(!prop_spec) { g_warning("%s: no type property named `%s'", G_STRLOC, name); break; } if(!(prop_spec->flags & G_PARAM_READABLE)) { g_warning("%s: type property `%s' is not readable", G_STRLOC, prop_spec->name); break; } g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(prop_spec)); type_get_property(type, prop_spec, &value, object); G_VALUE_LCOPY(&value, args, 0, &error); if(error) { g_warning("%s: %s", G_STRLOC, error); g_free(error); g_value_unset(&value); break; } g_value_unset(&value); name = va_arg(args, char *); } } /** * ipatch_type_object_get_property: * @object: Object instance to get type property from * @property_name: Name of property to get * @value: (out): An initialized value to store the property value in. The value's * type must be a type that the property can be transformed to. * * Get a single type property from an @object instance. */ void ipatch_type_object_get_property(GObject *object, const char *property_name, GValue *value) { GParamSpec *prop_spec; GType type; g_return_if_fail(G_IS_OBJECT(object)); g_return_if_fail(property_name != NULL); g_return_if_fail(G_IS_VALUE(value)); type = G_OBJECT_TYPE(object); g_return_if_fail(type != 0); prop_spec = ipatch_type_find_property(property_name); if(!prop_spec) g_warning("%s: no type property named `%s'", G_STRLOC, property_name); else if(!(prop_spec->flags & G_PARAM_READABLE)) g_warning("%s: type property `%s' is not readable", G_STRLOC, prop_spec->name); else { GValue *prop_value, tmp_value = { 0, }; /* auto-conversion of the callers value type */ if(G_VALUE_TYPE(value) == G_PARAM_SPEC_VALUE_TYPE(prop_spec)) { g_value_reset(value); prop_value = value; } else if(!g_value_type_transformable(G_PARAM_SPEC_VALUE_TYPE(prop_spec), G_VALUE_TYPE(value))) { g_warning("can't retrieve type property `%s' of type" " `%s' as value of type `%s'", prop_spec->name, g_type_name(G_PARAM_SPEC_VALUE_TYPE(prop_spec)), G_VALUE_TYPE_NAME(value)); return; } else { g_value_init(&tmp_value, G_PARAM_SPEC_VALUE_TYPE(prop_spec)); prop_value = &tmp_value; } type_get_property(type, prop_spec, prop_value, object); if(prop_value != value) { g_value_transform(prop_value, value); g_value_unset(&tmp_value); } } } /** * ipatch_type_set_dynamic_func: (skip) * @type: GType of the type property * @property_name: Name of a previously registered type property * @func: Callback function used for getting values for this type property * * Registers a callback function for dynamically getting the value of a * type property. * * Example: A dynamic property is created for SoundFont presets to return a * different "virtual-parent-type" depending on if its a percussion or * melodic preset (determined from a preset's bank number). */ void ipatch_type_set_dynamic_func(GType type, const char *property_name, IpatchTypePropGetFunc func) { ipatch_type_set_dynamic_func_full(type, property_name, func, NULL, NULL); } /** * ipatch_type_set_dynamic_func_full: (rename-to ipatch_type_set_dynamic_func) * @type: GType of the type property * @property_name: Name of a previously registered type property * @func: Callback function used for getting values for this type property * @notify_func: (nullable) (scope async) (closure user_data): Destroy function * callback when @func is removed * @user_data: (nullable): Data passed to @notify_func * * Registers a callback function for dynamically getting the value of a * type property. Like ipatch_type_set_dynamic_func() but more GObject Introspection * friendly. * * Example: A dynamic property is created for SoundFont presets to return a * different "virtual-parent-type" depending on if its a percussion or * melodic preset (determined from a preset's bank number). * * Since: 1.1.0 */ void ipatch_type_set_dynamic_func_full(GType type, const char *property_name, IpatchTypePropGetFunc func, GDestroyNotify notify_func, gpointer user_data) { GParamSpec *prop_spec; g_return_if_fail(type != 0); g_return_if_fail(property_name != NULL); prop_spec = ipatch_type_find_property(property_name); if(!prop_spec) { g_warning("%s: no type property named `%s'", G_STRLOC, property_name); return; } type_set_property(type, prop_spec, NULL, func, notify_func, user_data); } /** * ipatch_type_get_dynamic_func: (skip) * @type: GType of the type property * @property_name: Name of a previously registered type property * * Get a the dynamic function registered for a given @type and @property_name * with ipatch_type_set_dynamic_func(). Also can be used as an indicator of * whether a type property is dynamic or not. * * Returns: Pointer to the registered function or %NULL if no function * registered (not a dynamic type property). */ IpatchTypePropGetFunc ipatch_type_get_dynamic_func(GType type, const char *property_name) { GParamSpec *type_prop_pspec; TypePropValueKey key; TypePropValueVal *val; type_prop_pspec = ipatch_type_find_property(property_name); g_return_val_if_fail(type_prop_pspec != NULL, NULL); key.type = type; key.spec = type_prop_pspec; G_LOCK(type_prop_value_hash); val = g_hash_table_lookup(type_prop_value_hash, &key); G_UNLOCK(type_prop_value_hash); return (val ? val->func : NULL); } libinstpatch-1.1.6/libinstpatch/IpatchTypeProp.h000066400000000000000000000100701400263525300217510ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_TYPE_PROP_H__ #define __IPATCH_TYPE_PROP_H__ #include #include /* some built in type categories for "category" property */ enum { IPATCH_CATEGORY_NONE, /* a NULL value */ IPATCH_CATEGORY_BASE, /* patch file IpatchBase type */ IPATCH_CATEGORY_PROGRAM, /* a MIDI program type (MIDI locale) */ IPATCH_CATEGORY_INSTRUMENT, /* an instrument type (no MIDI locale) */ IPATCH_CATEGORY_INSTRUMENT_REF, /* a type referencing an Instrument */ IPATCH_CATEGORY_SAMPLE, /* sample type */ IPATCH_CATEGORY_SAMPLE_REF /* a type referencing a sample */ }; /* enum for "splits-type" to indicate that a type has key-range or velocity-range parameters or its children do */ typedef enum { IPATCH_SPLITS_NONE, /* type does not have splits */ IPATCH_SPLITS_NORMAL, /* normal splits */ IPATCH_SPLITS_NO_OVERLAP /* splits do not overlap */ } IpatchSplitsType; /** * IpatchTypePropGetFunc: * @type: The GType of the type property * @spec: The parameter specification * @value: Initialized value to store the type prop value in * @object: Object instance (might be %NULL) * * A function type used for active type property callbacks. * Allows for dynamic type properties that can return values depending * on an object's state. */ typedef void (*IpatchTypePropGetFunc)(GType type, GParamSpec *spec, GValue *value, GObject *object); void ipatch_type_install_property(GParamSpec *prop_spec); GParamSpec *ipatch_type_find_property(const char *name); GParamSpec **ipatch_type_list_properties(guint *n_properties); GType *ipatch_type_find_types_with_property(const char *name, const GValue *value, guint *n_types); void ipatch_type_set(GType type, const char *first_property_name, ...); void ipatch_type_set_valist(GType type, const char *first_property_name, va_list args); void ipatch_type_set_property(GType type, const char *property_name, const GValue *value); void ipatch_type_unset_property(GType type, const char *property_name); void ipatch_type_get(GType type, const char *first_property_name, ...); void ipatch_type_get_valist(GType type, const char *first_property_name, va_list args); void ipatch_type_get_property(GType type, const char *property_name, GValue *value); void ipatch_type_object_get(GObject *object, const char *first_property_name, ...); void ipatch_type_object_get_valist(GObject *object, const char *first_property_name, va_list args); void ipatch_type_object_get_property(GObject *object, const char *property_name, GValue *value); void ipatch_type_set_dynamic_func(GType type, const char *property_name, IpatchTypePropGetFunc func); void ipatch_type_set_dynamic_func_full(GType type, const char *property_name, IpatchTypePropGetFunc func, GDestroyNotify notify_func, gpointer user_data); IpatchTypePropGetFunc ipatch_type_get_dynamic_func(GType type, const char *property_name); #endif libinstpatch-1.1.6/libinstpatch/IpatchUnit.c000066400000000000000000000446601400263525300211150ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchUnit * @short_description: Unit conversion system * @see_also: * @stability: Stable * * System for registering unit types and conversion functions. */ #include #include #include #include #include "IpatchUnit.h" #include "IpatchRange.h" #include "ipatch_priv.h" #include "i18n.h" /* first dynamic unit type ID */ #define IPATCH_UNIT_TYPE_FIRST_DYNAMIC_ID 1024 typedef struct { IpatchValueTransform func; GDestroyNotify notify_func; gpointer user_data; } ConversionHashVal; static void ipatch_unit_conversion_hash_val_destroy(gpointer data); static ConversionHashVal *ipatch_unit_conversion_hash_val_new(void); static void ipatch_unit_conversion_hash_val_free(ConversionHashVal *val); G_LOCK_DEFINE_STATIC(unit_info); static GHashTable *unit_id_hash = NULL; /* unit registry (id) */ static GHashTable *unit_name_hash = NULL; /* secondary key (name -> id) */ /* unit class mappings (srcId:classType -> destInfo) */ static GHashTable *class_map_hash = NULL; static GHashTable *conversion_hash = NULL; /* conversion function hash */ /* next dynamic unit type ID (increment) */ static guint16 last_unit_id = IPATCH_UNIT_TYPE_FIRST_DYNAMIC_ID; void _ipatch_unit_generic_init(void); void _ipatch_unit_dls_init(void); void _ipatch_unit_sf2_init(void); /*----------------------------------------------------------------------------- Initialization/deinitialization of Unit conversion system ----------------------------------------------------------------------------*/ /** * _ipatch_unit_init: (skip) * * Initialize unit system */ void _ipatch_unit_init (void) { last_unit_id = IPATCH_UNIT_TYPE_FIRST_DYNAMIC_ID; /* Create hash table to register unit by id */ unit_id_hash = g_hash_table_new_full( NULL, NULL, NULL, (GDestroyNotify)ipatch_unit_info_free); /* Create hash table to register unit by name */ unit_name_hash = g_hash_table_new (g_str_hash, g_str_equal); class_map_hash = g_hash_table_new (NULL, NULL); /* Create the conversion hash table to register conversion function */ conversion_hash = g_hash_table_new_full (NULL, NULL, NULL, ipatch_unit_conversion_hash_val_destroy); /* initialize unit types and conversion handlers */ _ipatch_unit_generic_init (); _ipatch_unit_dls_init (); _ipatch_unit_sf2_init (); } /** * _ipatch_unit_deinit: (skip) * * Free unit system */ void _ipatch_unit_deinit(void) { g_hash_table_destroy(unit_id_hash); g_hash_table_destroy(unit_name_hash); g_hash_table_destroy(class_map_hash); g_hash_table_destroy(conversion_hash); } /* ----- Unit convertion system functions ---------------------------------*/ static void ipatch_unit_conversion_hash_val_destroy(gpointer data) { ConversionHashVal *val = data; if(val->notify_func) { val->notify_func(val->user_data); } ipatch_unit_conversion_hash_val_free(val); } static ConversionHashVal * ipatch_unit_conversion_hash_val_new(void) { return (g_slice_new(ConversionHashVal)); } static void ipatch_unit_conversion_hash_val_free(ConversionHashVal *val) { g_slice_free(ConversionHashVal, val); } GType ipatch_unit_info_get_type(void) { static GType type = 0; if(!type) type = g_boxed_type_register_static("IpatchUnitInfo", (GBoxedCopyFunc)ipatch_unit_info_duplicate, (GBoxedFreeFunc)ipatch_unit_info_free); return (type); } /** * ipatch_unit_info_new: (skip) * * Allocate a unit info structure for registering unit types with * ipatch_unit_register(). Using this function should minimize API changes * if additional fields are added to #IpatchUnitInfo. Free the returned * structure with ipatch_unit_free() when finished registering unit types. * * Returns: The newly allocated unit info structure. */ IpatchUnitInfo * ipatch_unit_info_new(void) { return (g_slice_new0(IpatchUnitInfo)); } /** * ipatch_unit_info_free: (skip) * @info: Unit info to free * * Free a unit info structure that was created with ipatch_unit_info_new(). */ void ipatch_unit_info_free(IpatchUnitInfo *info) { g_slice_free(IpatchUnitInfo, info); } /** * ipatch_unit_info_duplicate: (skip) * @info: Unit info to duplicate * * Duplicate a unit info structure. * * Returns: Newly allocated duplicate unit info structure * * Since: 1.1.0 */ IpatchUnitInfo * ipatch_unit_info_duplicate(const IpatchUnitInfo *info) { IpatchUnitInfo *new; new = ipatch_unit_info_new(); memcpy(new, info, sizeof(IpatchUnitInfo)); return (new); } /** * ipatch_unit_register: * @info: (transfer none): Unit info (shallow copied) * * Add a new unit type to the unit registry. Note that the @info structure * is shallow copied, so strings should be constant or guaranteed to not * be freed. If the id field is already set * (non-zero) in @info, then it is used (should be 0 for dynamic unit * types). If the label field of the @info * structure is %NULL then it is set to the i18n translated string for * name. Unit types can not be un-registered. * Unit IDs of dynamic (non built-in types) should not be relied apon to * always be the same between program executions. * * Returns: New unit ID */ guint16 ipatch_unit_register(const IpatchUnitInfo *info) { IpatchUnitInfo *newinfo; g_return_val_if_fail(info != NULL, 0); g_return_val_if_fail(info->name != NULL, 0); /* allocate the new unit info, so that the pointer is constant and can be * returned to the user out of lock (not safe if it was in a GArray) */ newinfo = ipatch_unit_info_new(); *newinfo = *info; /* if label not set, use i18n translated name */ if(!info->label) { newinfo->label = _(info->name); } G_LOCK(unit_info); if(!newinfo->id) { newinfo->id = last_unit_id++; } g_hash_table_insert(unit_id_hash, /* hash by id */ GUINT_TO_POINTER((guint32)(newinfo->id)), newinfo); g_hash_table_insert(unit_name_hash, newinfo->name, newinfo); /* hash by name */ G_UNLOCK(unit_info); return (newinfo->id); } /** * ipatch_unit_lookup: * @id: Unit ID * * Looks up unit info by ID. * * Returns: (transfer none): Unit info structure with @id or %NULL if not found, * returned structure is internal and should not be modified or freed */ IpatchUnitInfo * ipatch_unit_lookup(guint16 id) { IpatchUnitInfo *info; G_LOCK(unit_info); info = g_hash_table_lookup(unit_id_hash, GUINT_TO_POINTER((guint32)id)); G_UNLOCK(unit_info); return (info); } /** * ipatch_unit_lookup_by_name: * @name: Unit name identifier * * Looks up unit info by name. * * Returns: (transfer none): Unit info structure with @name or %NULL if not found, * returned structure is internal and should not be modified or freed */ IpatchUnitInfo * ipatch_unit_lookup_by_name(const char *name) { IpatchUnitInfo *info; G_LOCK(unit_info); info = g_hash_table_lookup(unit_name_hash, name); G_UNLOCK(unit_info); return (info); } /** * ipatch_unit_class_register_map: * @class_type: (type IpatchUnitClassType): Class type (see #IpatchUnitClassType) * @src_units: Source unit type of mapping * @dest_units: Destination unit type for this map * * Register a unit class mapping. Unit class types define domains of * conversion, an example is the "user" unit class * (#IPATCH_UNIT_CLASS_USER) which is used to convert values to units * digestable by a human. A conversion class is essentially a mapping * between unit types, which can then be used to lookup conversion * functions. */ void ipatch_unit_class_register_map(guint16 class_type, guint16 src_units, guint16 dest_units) { IpatchUnitInfo *src_info, *dest_info; guint32 hashval; g_return_if_fail(class_type > IPATCH_UNIT_CLASS_NONE); g_return_if_fail(class_type < IPATCH_UNIT_CLASS_COUNT); hashval = class_type | (src_units << 16); G_LOCK(unit_info); src_info = g_hash_table_lookup(unit_id_hash, GUINT_TO_POINTER((guint32)src_units)); dest_info = g_hash_table_lookup(unit_id_hash, GUINT_TO_POINTER((guint32)dest_units)); /* only insert map if unit types are valid */ if(src_info != NULL && dest_info != NULL) { g_hash_table_insert(class_map_hash, GUINT_TO_POINTER(hashval), dest_info); } G_UNLOCK(unit_info); g_return_if_fail(src_info != NULL); g_return_if_fail(dest_info != NULL); } /** * ipatch_unit_class_lookup_map: * @class_type: (type IpatchUnitClassType): Class type (see #IpatchUnitClassType) * @src_units: Source unit type of mapping to lookup * * Lookup a unit class mapping (see ipatch_unit_class_register_map ()). * * Returns: (transfer none): Pointer to destination unit info structure, * or %NULL if not found. Returned structure is internal and should not * be modified or freed. */ IpatchUnitInfo * ipatch_unit_class_lookup_map(guint16 class_type, guint16 src_units) { IpatchUnitInfo *dest_info; guint32 hashval; g_return_val_if_fail(class_type > IPATCH_UNIT_CLASS_NONE, 0); g_return_val_if_fail(class_type < IPATCH_UNIT_CLASS_COUNT, 0); hashval = class_type | (src_units << 16); G_LOCK(unit_info); dest_info = g_hash_table_lookup(class_map_hash, GUINT_TO_POINTER(hashval)); G_UNLOCK(unit_info); return (dest_info); } /** * ipatch_unit_conversion_register: (skip) * @src_units: Source unit type * @dest_units: Destination unit type * @func: Conversion function handler or %NULL for unity conversion (the * value type will be converted but not the actual value, example: * float -> int). * * Register a parameter unit conversion function. */ void ipatch_unit_conversion_register(guint16 src_units, guint16 dest_units, IpatchValueTransform func) { ipatch_unit_conversion_register_full(src_units, dest_units, func, NULL, NULL); } /** * ipatch_unit_conversion_register_full: (rename-to ipatch_unit_conversion_register) * @src_units: Source unit type * @dest_units: Destination unit type * @func: Conversion function handler or %NULL for unity conversion (the * value type will be converted but not the actual value, example: float to int). * @notify_func: (nullable) (scope async) (closure user_data): Destroy notification * when conversion function is removed * @user_data: (nullable): Data to pass to @notify_func callback * * Register a parameter unit conversion function. Like ipatch_unit_conversion_register() * but friendlier to GObject Introspection. * * Since: 1.1.0 */ void ipatch_unit_conversion_register_full(guint16 src_units, guint16 dest_units, IpatchValueTransform func, GDestroyNotify notify_func, gpointer user_data) { ConversionHashVal *val; guint32 hashkey; hashkey = src_units | (dest_units << 16); val = ipatch_unit_conversion_hash_val_new(); // ++ alloc val->func = func; val->notify_func = notify_func; val->user_data = user_data; G_LOCK(unit_info); // !! hash takes over value g_hash_table_insert(conversion_hash, GUINT_TO_POINTER(hashkey), val); G_UNLOCK(unit_info); } /** * ipatch_unit_conversion_lookup: (skip) * @src_units: Source unit type * @dest_units: Destination unit type * @set: Location to store a boolean value indicating if the conversion is * set, to differentiate between a %NULL conversion function and an invalid * conversion. Can be %NULL in which case this parameter is ignored. * * Lookup a conversion function by source and destination unit types. * * Returns: Conversion function pointer or %NULL if a unity conversion or * no matching handlers (use @set to determine which). */ IpatchValueTransform ipatch_unit_conversion_lookup(guint16 src_units, guint16 dest_units, gboolean *set) { ConversionHashVal *hashval; IpatchValueTransform func; guint32 hashkey; hashkey = src_units | (dest_units << 16); G_LOCK(unit_info); hashval = g_hash_table_lookup(conversion_hash, GUINT_TO_POINTER(hashkey)); if(hashval) { func = hashval->func; } G_UNLOCK(unit_info); if(set) { *set = hashval != NULL; } return (hashval ? func : NULL); } /** * ipatch_unit_convert: * @src_units: Source unit type ID * @dest_units: Destination unit type ID * @src_val: Source value (type should be compatible with the source unit's * value type) * @dest_val: (transfer none): Destination value (value should be initialized to a type that is * compatible with the destination unit's value type) * * Convert a value from one unit type to another. * * Returns: %TRUE if value was successfully converted, %FALSE * otherwise (the only reasons for failure are invalid function * parameters, no conversion function for the given unit types, or * incompatible GValue types in conversion, therefore the return value * can be safely ignored if the caller is sure the parameters and * types are OK). */ gboolean ipatch_unit_convert(guint16 src_units, guint16 dest_units, const GValue *src_val, GValue *dest_val) { IpatchValueTransform convert_func; IpatchUnitInfo *src_info, *dest_info; ConversionHashVal *unit_converter; // More descriptive for g_return_val_if_fail const GValue *sv; GValue tmpsv = { 0 }, tmpdv = { 0 }, *dv; guint32 hashkey; gboolean retval; hashkey = src_units | (dest_units << 16); G_LOCK(unit_info); src_info = g_hash_table_lookup(unit_id_hash, GUINT_TO_POINTER((guint32)src_units)); dest_info = g_hash_table_lookup(unit_id_hash, GUINT_TO_POINTER((guint32)dest_units)); unit_converter = g_hash_table_lookup(conversion_hash, GUINT_TO_POINTER(hashkey)); convert_func = unit_converter ? unit_converter->func : NULL; G_UNLOCK(unit_info); g_return_val_if_fail(src_info != NULL, FALSE); g_return_val_if_fail(dest_info != NULL, FALSE); g_return_val_if_fail(unit_converter != NULL, FALSE); if(G_UNLIKELY(!convert_func)) /* unity conversion? */ { retval = g_value_transform(src_val, dest_val); if(G_UNLIKELY(!retval)) { g_critical("%s: Failed to transform value type '%s' to type '%s'", G_STRLOC, g_type_name(G_VALUE_TYPE(src_val)), g_type_name(G_VALUE_TYPE(dest_val))); return (FALSE); } return (TRUE); } /* source value needs to be transformed? */ if(G_UNLIKELY(G_VALUE_TYPE(src_val) != src_info->value_type)) { g_value_init(&tmpsv, src_info->value_type); retval = g_value_transform(src_val, &tmpsv); sv = &tmpsv; if(G_UNLIKELY(!retval)) { g_value_unset(&tmpsv); g_critical("%s: Failed to transform value type '%s' to type '%s'", G_STRLOC, g_type_name(G_VALUE_TYPE(src_val)), g_type_name(src_info->value_type)); return (FALSE); } } else { sv = src_val; /* same type, just use it */ } /* destination value needs to be transformed? */ if(G_LIKELY(G_VALUE_TYPE(dest_val) == dest_info->value_type)) { /* same type, just reset value and use it */ g_value_reset(dest_val); dv = dest_val; } else if(!g_value_type_transformable(dest_info->value_type, G_VALUE_TYPE(dest_val))) { g_critical("%s: Failed to transform value type '%s' to type '%s'", G_STRLOC, g_type_name(dest_info->value_type), g_type_name(G_VALUE_TYPE(dest_val))); return (FALSE); } else /* initialize temp value to native type (transformed later) */ { g_value_init(&tmpdv, dest_info->value_type); dv = &tmpdv; } /* do the conversion */ (*convert_func)(sv, dv); /* free the converted source value if needed */ if(G_UNLIKELY(sv == &tmpsv)) { g_value_unset(&tmpsv); } if(G_UNLIKELY(dv == &tmpdv)) /* transform the destination value if needed */ { g_value_transform(dv, dest_val); g_value_unset(&tmpdv); } return (TRUE); } /** * ipatch_unit_user_class_convert: * @src_units: Source unit type ID * @src_val: Source value (type should be compatible with the source unit's * value type) * * Converts a value to "user" units. User units are unit types that * are adequate for human consumption. The #IPATCH_UNIT_CLASS_USER * map is used to lookup the corresponding user type to convert to. * Not all unit types have an associated user type or the @src_units * type can itself be a user type; in either of these cases the * @src_val is converted as is (possibly converted from another value * type to double). * * Returns: The value converted to user units. */ double ipatch_unit_user_class_convert(guint16 src_units, const GValue *src_val) { IpatchUnitInfo *dest_info; guint16 dest_units; GValue v = { 0 }; double retval; g_return_val_if_fail(src_val != NULL, 0.0); dest_info = ipatch_unit_class_lookup_map(IPATCH_UNIT_CLASS_USER, src_units); dest_units = dest_info ? dest_info->id : src_units; g_value_init(&v, G_TYPE_DOUBLE); ipatch_unit_convert(src_units, dest_units, src_val, &v); retval = g_value_get_double(&v); g_value_unset(&v); /* probably not necessary, but its the right way (TM) */ return (retval); } libinstpatch-1.1.6/libinstpatch/IpatchUnit.h000066400000000000000000000116131400263525300211120ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_UNIT_H__ #define __IPATCH_UNIT_H__ #include #include typedef struct _IpatchUnitInfo IpatchUnitInfo; /* structure defining a unit type */ struct _IpatchUnitInfo { guint16 id; /* unit type ID */ guint8 digits; /* significant digits to display to user */ guint8 flags; /* IpatchUnitFlags */ GType value_type; /* unit value type */ char *name; /* name identifier (constant) */ char *label; /* unit label (translated) */ char *descr; /* unit description (translated) */ }; typedef enum { IPATCH_UNIT_LOGARITHMIC = 1 << 0, /* unit is logarithmic */ IPATCH_UNIT_USER = 1 << 1 /* a user friendly unit type */ } IpatchUnitFlags; /** * IpatchValueTransform: * @src: Source value to transform from * @dest: Destination value to transform to * * Transform from one value to another. The @src and @dest values have * already been initialized to specific types and the transform function * should convert/process them as necessary. */ typedef void (*IpatchValueTransform)(const GValue *src, GValue *dest); /* built-in unit types */ typedef enum { IPATCH_UNIT_TYPE_NONE = 0, /* a NULL value */ IPATCH_UNIT_TYPE_INT = 1, IPATCH_UNIT_TYPE_UINT = 2, IPATCH_UNIT_TYPE_RANGE = 3, IPATCH_UNIT_TYPE_DECIBELS = 4, IPATCH_UNIT_TYPE_PERCENT = 5, IPATCH_UNIT_TYPE_SEMITONES = 6, IPATCH_UNIT_TYPE_CENTS = 7, IPATCH_UNIT_TYPE_TIME_CENTS = 8, IPATCH_UNIT_TYPE_SAMPLE_RATE = 9, IPATCH_UNIT_TYPE_SAMPLES = 10, IPATCH_UNIT_TYPE_HERTZ = 11, IPATCH_UNIT_TYPE_SECONDS = 12, IPATCH_UNIT_TYPE_MULTIPLIER = 13, /* 128 - 159 reserved for IpatchUnit_DLS.h */ IPATCH_UNIT_TYPE_DLS_GAIN = 128, IPATCH_UNIT_TYPE_DLS_ABS_TIME = 129, IPATCH_UNIT_TYPE_DLS_REL_TIME = 130, IPATCH_UNIT_TYPE_DLS_ABS_PITCH = 131, IPATCH_UNIT_TYPE_DLS_REL_PITCH = 132, IPATCH_UNIT_TYPE_DLS_PERCENT = 133, /* 160 - 169 reserved for IpatchUnit_SF2.h */ IPATCH_UNIT_TYPE_SF2_ABS_PITCH = 160, IPATCH_UNIT_TYPE_SF2_OFS_PITCH = 161, IPATCH_UNIT_TYPE_SF2_ABS_TIME = 162, IPATCH_UNIT_TYPE_SF2_OFS_TIME = 163, IPATCH_UNIT_TYPE_CENTIBELS = 164, IPATCH_UNIT_TYPE_32K_SAMPLES = 165, IPATCH_UNIT_TYPE_TENTH_PERCENT = 166 } IpatchUnitType; /* * Unit class types define domains of conversion, an example is the "user" * unit class which is used to convert values to units digestable by a human. * A conversion class is essentially a mapping between unit types, which can * then be used to lookup conversion functions. */ typedef enum { IPATCH_UNIT_CLASS_NONE, /* a NULL value */ IPATCH_UNIT_CLASS_USER, /* "user" conversion class (for humans) */ IPATCH_UNIT_CLASS_DLS, /* DLS (native patch type) class */ IPATCH_UNIT_CLASS_COUNT /* NOT A VALID CLASS - count of classes */ } IpatchUnitClassType; GType ipatch_unit_info_get_type(void); IpatchUnitInfo *ipatch_unit_info_new(void); void ipatch_unit_info_free(IpatchUnitInfo *info); IpatchUnitInfo *ipatch_unit_info_duplicate(const IpatchUnitInfo *info); guint16 ipatch_unit_register(const IpatchUnitInfo *info); IpatchUnitInfo *ipatch_unit_lookup(guint16 id); IpatchUnitInfo *ipatch_unit_lookup_by_name(const char *name); void ipatch_unit_class_register_map(guint16 class_type, guint16 src_units, guint16 dest_units); IpatchUnitInfo *ipatch_unit_class_lookup_map(guint16 class_type, guint16 src_units); void ipatch_unit_conversion_register(guint16 src_units, guint16 dest_units, IpatchValueTransform func); void ipatch_unit_conversion_register_full(guint16 src_units, guint16 dest_units, IpatchValueTransform func, GDestroyNotify notify_func, gpointer user_data); IpatchValueTransform ipatch_unit_conversion_lookup(guint16 src_units, guint16 dest_units, gboolean *set); gboolean ipatch_unit_convert(guint16 src_units, guint16 dest_units, const GValue *src_val, GValue *dest_val); double ipatch_unit_user_class_convert(guint16 src_units, const GValue *src_val); #endif libinstpatch-1.1.6/libinstpatch/IpatchUnit_DLS.c000066400000000000000000000354541400263525300216200ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchUnit_DLS * @short_description: Unit types and conversions for DLS * @see_also: * @stability: Stable */ #include #include #include #include #include "IpatchUnit_DLS.h" #include "IpatchUnit.h" #include "i18n.h" static void ipatch_unit_dls_percent_to_percent_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_percent_to_dls_percent_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_dls_gain_to_decibels_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_decibels_to_dls_gain_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_dls_abs_time_to_seconds_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_seconds_to_dls_abs_time_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_dls_rel_time_to_time_cents_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_time_cents_to_dls_rel_time_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_dls_abs_pitch_to_hertz_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_hertz_to_dls_abs_pitch_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_dls_rel_pitch_to_cents_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_cents_to_dls_rel_pitch_value(const GValue *src_val, GValue *dest_val); /** * _ipatch_unit_dls_init: (skip) */ void _ipatch_unit_dls_init(void) { IpatchUnitInfo *info; info = ipatch_unit_info_new(); info->label = NULL; info->descr = NULL; info->value_type = G_TYPE_INT; info->digits = 0; info->id = IPATCH_UNIT_TYPE_DLS_PERCENT; info->name = "DLSPercent"; ipatch_unit_register(info); info->id = IPATCH_UNIT_TYPE_DLS_GAIN; info->name = "DLSGain"; ipatch_unit_register(info); info->id = IPATCH_UNIT_TYPE_DLS_ABS_TIME; info->name = "DLSAbsTime"; ipatch_unit_register(info); info->id = IPATCH_UNIT_TYPE_DLS_REL_TIME; info->name = "DLSRelTime"; ipatch_unit_register(info); info->id = IPATCH_UNIT_TYPE_DLS_ABS_PITCH; info->name = "DLSAbsPitch"; ipatch_unit_register(info); info->id = IPATCH_UNIT_TYPE_DLS_REL_PITCH; info->name = "DLSRelPitch"; ipatch_unit_register(info); ipatch_unit_info_free(info); /* done with unit info structure, free it */ ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_DLS_PERCENT, IPATCH_UNIT_TYPE_PERCENT, ipatch_unit_dls_percent_to_percent_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_PERCENT, IPATCH_UNIT_TYPE_DLS_PERCENT, ipatch_unit_percent_to_dls_percent_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_DLS_GAIN, IPATCH_UNIT_TYPE_DECIBELS, ipatch_unit_dls_gain_to_decibels_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_DECIBELS, IPATCH_UNIT_TYPE_DLS_GAIN, ipatch_unit_decibels_to_dls_gain_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_DLS_ABS_TIME, IPATCH_UNIT_TYPE_SECONDS, ipatch_unit_dls_abs_time_to_seconds_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_SECONDS, IPATCH_UNIT_TYPE_DLS_ABS_TIME, ipatch_unit_seconds_to_dls_abs_time_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_DLS_REL_TIME, IPATCH_UNIT_TYPE_TIME_CENTS, ipatch_unit_dls_rel_time_to_time_cents_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_TIME_CENTS, IPATCH_UNIT_TYPE_DLS_REL_TIME, ipatch_unit_time_cents_to_dls_rel_time_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_DLS_ABS_PITCH, IPATCH_UNIT_TYPE_HERTZ, ipatch_unit_dls_abs_pitch_to_hertz_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_HERTZ, IPATCH_UNIT_TYPE_DLS_ABS_PITCH, ipatch_unit_hertz_to_dls_abs_pitch_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_DLS_REL_PITCH, IPATCH_UNIT_TYPE_CENTS, ipatch_unit_dls_rel_pitch_to_cents_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_CENTS, IPATCH_UNIT_TYPE_DLS_REL_PITCH, ipatch_unit_cents_to_dls_rel_pitch_value); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_USER, IPATCH_UNIT_TYPE_DLS_PERCENT, IPATCH_UNIT_TYPE_PERCENT); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_USER, IPATCH_UNIT_TYPE_DLS_GAIN, IPATCH_UNIT_TYPE_DECIBELS); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_USER, IPATCH_UNIT_TYPE_DLS_ABS_TIME, IPATCH_UNIT_TYPE_SECONDS); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_USER, IPATCH_UNIT_TYPE_DLS_REL_TIME, IPATCH_UNIT_TYPE_TIME_CENTS); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_USER, IPATCH_UNIT_TYPE_DLS_ABS_PITCH, IPATCH_UNIT_TYPE_HERTZ); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_USER, IPATCH_UNIT_TYPE_DLS_REL_PITCH, IPATCH_UNIT_TYPE_CENTS); } /** * ipatch_unit_dls_class_convert: * @src_units: Source unit type ID * @src_val: Source value (type should be compatible with the source unit's * value type) * * Converts a value to "DLS" units. DLS units are unit types that * are used by DLS (Downloadable Sounds) patches. The #IPATCH_UNIT_CLASS_DLS * map is used to lookup the corresponding type to convert to. * Only some types have an associated DLS type. It is an error to pass a * @src_units type that has no DLS mapping (note that this is contrary to the * behavior of ipatch_unit_user_class_convert()). * * Returns: The value converted to DLS units. */ int ipatch_unit_dls_class_convert(guint16 src_units, const GValue *src_val) { IpatchUnitInfo *dest_info; GValue v = { 0 }; int retval; g_return_val_if_fail(src_val != NULL, 0); dest_info = ipatch_unit_class_lookup_map(IPATCH_UNIT_CLASS_DLS, src_units); g_return_val_if_fail(dest_info != NULL, 0); g_value_init(&v, G_TYPE_INT); ipatch_unit_convert(src_units, dest_info->id, src_val, &v); retval = g_value_get_int(&v); g_value_unset(&v); /* probably not needed, for the sake of extra paranoia (TM) */ return (retval); } /** * ipatch_unit_dls_percent_to_percent: * @dls_percent: Value in DLS percent units * * Convert value in DLS percent units to percent. * * percent = dls_percent / (10 * 65536) * * Returns: Value in percent */ double ipatch_unit_dls_percent_to_percent(int dls_percent) { return ((double)dls_percent / 655360.0); } /** * ipatch_unit_percent_to_dls_percent: * @percent: Value in percent * * Convert percent to DLS percent. * * dls_percent = percent * 10 * 65536 * * Returns: Converted integer in DLS percent */ int ipatch_unit_percent_to_dls_percent(double percent) { return (int)(percent * 655360.0 + 0.5); /* +0.5 for rounding */ } /** * ipatch_unit_dls_gain_to_decibels: * @dls_gain: Value in DLS gain units * * Converts a value from DLS gain to decibels. * * dls_gain = 200 * 65536 * log10 (V / v) * decibels = 20 * log10 (V / v) * * Returns: Value converted to decibels */ double ipatch_unit_dls_gain_to_decibels(int dls_gain) { return ((double)dls_gain / 655360.0); } /** * ipatch_unit_decibels_to_dls_gain: * @db: Value in decibels * * Converts a value from decibels to DLS gain. * See ipatch_unit_dls_gain_to_decibel() * * Returns: Value converted to DLS gain */ int ipatch_unit_decibels_to_dls_gain(double db) { return (int)(db * 655360.0 + 0.5); } /** * ipatch_unit_dls_abs_time_to_seconds: * @dls_abs_time: Value in DLS absolute time * * Converts a value from DLS absolute time to seconds. * seconds = 2^(dls_abs_time / (1200 * 65536)) * * 0x80000000 is used as a 0 value. * * Returns: Value converted to seconds */ double ipatch_unit_dls_abs_time_to_seconds(gint32 dls_abs_time) { if(dls_abs_time == IPATCH_UNIT_DLS_ABS_TIME_0SECS) { return (0.0); } return (pow(2.0, (double)dls_abs_time / (1200 * 65536))); } /** * ipatch_unit_seconds_to_dls_abs_time: * @seconds: Value in seconds * * Converts a value from seconds to DLS absolute time. * dls_rel_time = 1200 * log2 (seconds) * 65536 * * Returns: Value converted to DLS relative time */ gint32 ipatch_unit_seconds_to_dls_abs_time(double seconds) { if(seconds == 0.0) { return (IPATCH_UNIT_DLS_ABS_TIME_0SECS); } return (gint32)((double)1200.0 * (log(seconds) / log(2.0)) * 65536.0 + 0.5); } /** * ipatch_unit_dls_rel_time_to_time_cents: * @dls_rel_time: Value in DLS relative time * * Converts a value from DLS relative time to time cents. * time_cents = dls_rel_time / 65536 * * Returns: Value converted to time cents */ double ipatch_unit_dls_rel_time_to_time_cents(int dls_rel_time) { return (dls_rel_time / 65536.0); } /** * ipatch_unit_time_cents_to_dls_rel_time: * @time_cents: Value in time cents * * Converts a value from time_cents to DLS relative time. * dls_rel_time = time_cents * 65536 * * Returns: Value converted to DLS relative time */ int ipatch_unit_time_cents_to_dls_rel_time(double time_cents) { return (int)(time_cents * 65536.0 + 0.5); } /** * ipatch_unit_dls_abs_pitch_to_hertz: * @dls_abs_pitch: Value in DLS absolute pitch * * Converts a value from DLS absolute pitch to hertz. * hertz = 440 * 2^((dls_abs_pitch / 65536 - 6900) / 1200) * * Returns: Value converted to hertz */ double ipatch_unit_dls_abs_pitch_to_hertz(int dls_abs_pitch) { return ((double)440.0 * pow(2.0, (dls_abs_pitch / 65536.0 - 6900.0) / 1200.0)); } /** * ipatch_unit_hertz_to_dls_abs_pitch: * @hertz: Value in hertz * * Converts a value from hertz to DLS absolute pitch. * dls_abs_pitch = (1200 * log2(hertz/440) + 6900) * 65536 * * Returns: Value converted to DLS absolute pitch */ int ipatch_unit_hertz_to_dls_abs_pitch(double hertz) { return (int)(((double)1200.0 * (log(hertz / 440.0) / log(2)) + 6900.0) * 65536.0 + 0.5); } /** * ipatch_unit_dls_rel_pitch_to_cents: * @dls_rel_pitch: Value in DLS relative pitch * * Converts a value from DLS relative pitch to cents. * cents = dls_rel_pitch / 65536 * * Returns: Value converted to cents */ double ipatch_unit_dls_rel_pitch_to_cents(int dls_rel_pitch) { return ((double)dls_rel_pitch / 65536.0); } /** * ipatch_unit_cents_to_dls_rel_pitch: * @cents: Value in cents * * Converts a value from cents to DLS relative pitch. * dls_rel_pitch = cents * 65536 * * Returns: Value converted to DLS relative pitch */ int ipatch_unit_cents_to_dls_rel_pitch(double cents) { return (int)(cents * 65536.0 + 0.5); } /* ================================================= GValue conversion functions, duplicated for speed ================================================= */ static void ipatch_unit_dls_percent_to_percent_value(const GValue *src_val, GValue *dest_val) { int dls_percent = g_value_get_int(src_val); g_value_set_double(dest_val, (double)dls_percent / 655360.0); } static void ipatch_unit_percent_to_dls_percent_value(const GValue *src_val, GValue *dest_val) { double percent = g_value_get_double(src_val); g_value_set_int(dest_val, (gint)(percent * 655360.0 + 0.5)); } static void ipatch_unit_dls_gain_to_decibels_value(const GValue *src_val, GValue *dest_val) { int dls_gain = g_value_get_int(src_val); g_value_set_double(dest_val, (double)dls_gain / 655360.0); } static void ipatch_unit_decibels_to_dls_gain_value(const GValue *src_val, GValue *dest_val) { double db = g_value_get_double(src_val); g_value_set_int(dest_val, (gint)(db * 655360.0 + 0.5)); } static void ipatch_unit_dls_abs_time_to_seconds_value(const GValue *src_val, GValue *dest_val) { int dls_abs_time = g_value_get_int(src_val); double secs; if(dls_abs_time != IPATCH_UNIT_DLS_ABS_TIME_0SECS) { secs = pow(2.0, (double)dls_abs_time / (1200 * 65536)); } else { secs = 0.0; } g_value_set_double(dest_val, secs); } static void ipatch_unit_seconds_to_dls_abs_time_value(const GValue *src_val, GValue *dest_val) { double secs = g_value_get_double(src_val); int dls_abs_time; if(secs != 0.0) { dls_abs_time = (int)((double)1200.0 * (log(secs) / log(2.0)) * 65536.0 + 0.5); } else { dls_abs_time = IPATCH_UNIT_DLS_ABS_TIME_0SECS; } g_value_set_int(dest_val, dls_abs_time); } static void ipatch_unit_dls_rel_time_to_time_cents_value(const GValue *src_val, GValue *dest_val) { int dls_rel_time = g_value_get_int(src_val); g_value_set_double(dest_val, (double)dls_rel_time / 65536.0); } static void ipatch_unit_time_cents_to_dls_rel_time_value(const GValue *src_val, GValue *dest_val) { double time_cents = g_value_get_double(src_val); g_value_set_int(dest_val, (gint)(time_cents * 65536.0 + 0.5)); } static void ipatch_unit_dls_abs_pitch_to_hertz_value(const GValue *src_val, GValue *dest_val) { int dls_abs_pitch = g_value_get_int(src_val); g_value_set_double(dest_val, 440.0 * pow(2.0, ((double)dls_abs_pitch / 65536.0 - 6900.0) / 1200.0)); } static void ipatch_unit_hertz_to_dls_abs_pitch_value(const GValue *src_val, GValue *dest_val) { double hertz = g_value_get_double(src_val); g_value_set_int(dest_val, (gint)(((double)1200.0 * (log(hertz / 440.0) / log(2)) + 6900.0) * 65536.0 + 0.5)); } static void ipatch_unit_dls_rel_pitch_to_cents_value(const GValue *src_val, GValue *dest_val) { int dls_rel_pitch = g_value_get_int(src_val); g_value_set_double(dest_val, (double)dls_rel_pitch / 65536.0); } static void ipatch_unit_cents_to_dls_rel_pitch_value(const GValue *src_val, GValue *dest_val) { double cents = g_value_get_double(src_val); g_value_set_int(dest_val, (gint)(cents * 65536.0 + 0.5)); } libinstpatch-1.1.6/libinstpatch/IpatchUnit_DLS.h000066400000000000000000000034771400263525300216250ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_UNIT_DLS_H__ #define __IPATCH_UNIT_DLS_H__ #include #include /* Value for 0 seconds in DLS absolute time (a degenerate case) */ #define IPATCH_UNIT_DLS_ABS_TIME_0SECS ((gint32)0x80000000L) int ipatch_unit_dls_class_convert(guint16 src_units, const GValue *src_val); double ipatch_unit_dls_percent_to_percent(int dls_percent); int ipatch_unit_percent_to_dls_percent(double percent); double ipatch_unit_dls_gain_to_decibels(int dls_gain); int ipatch_unit_decibels_to_dls_gain(double db); double ipatch_unit_dls_abs_time_to_seconds(int dls_abs_time); int ipatch_unit_seconds_to_dls_abs_time(double seconds); double ipatch_unit_dls_rel_time_to_time_cents(int dls_rel_time); int ipatch_unit_time_cents_to_dls_rel_time(double time_cents); double ipatch_unit_dls_abs_pitch_to_hertz(int dls_abs_pitch); int ipatch_unit_hertz_to_dls_abs_pitch(double hertz); double ipatch_unit_dls_rel_pitch_to_cents(int dls_rel_pitch); int ipatch_unit_cents_to_dls_rel_pitch(double cents); #endif libinstpatch-1.1.6/libinstpatch/IpatchUnit_SF2.c000066400000000000000000000475101400263525300215640ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchUnit_SF2 * @short_description: Unit types and conversions for SoundFont * @see_also: * @stability: Stable */ #include #include #include #include #include "IpatchUnit_SF2.h" #include "IpatchUnit.h" #include "i18n.h" static void ipatch_unit_sf2_abs_pitch_to_dls_abs_pitch_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_dls_abs_pitch_to_sf2_abs_pitch_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_sf2_abs_pitch_to_hertz_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_hertz_to_sf2_abs_pitch_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_sf2_abs_time_to_dls_abs_time_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_dls_abs_time_to_sf2_abs_time_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_sf2_abs_time_to_seconds_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_seconds_to_sf2_abs_time_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_centibels_to_dls_gain_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_dls_gain_to_centibels_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_centibels_to_decibels_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_decibels_to_centibels_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_tenth_percent_to_percent_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_percent_to_tenth_percent_value(const GValue *src_val, GValue *dest_val); /** * _ipatch_unit_sf2_init: (skip) */ void _ipatch_unit_sf2_init(void) { IpatchUnitInfo *info; info = ipatch_unit_info_new(); info->digits = 0; info->label = NULL; info->descr = NULL; info->value_type = G_TYPE_INT; /* FIXME - SoundFont absolute pitch is the same as cents.. */ info->id = IPATCH_UNIT_TYPE_SF2_ABS_PITCH; info->name = "SF2AbsPitch"; ipatch_unit_register(info); info->id = IPATCH_UNIT_TYPE_SF2_OFS_PITCH; info->name = "SF2OfsPitch"; ipatch_unit_register(info); info->id = IPATCH_UNIT_TYPE_SF2_ABS_TIME; info->name = "SF2AbsTime"; ipatch_unit_register(info); info->id = IPATCH_UNIT_TYPE_SF2_OFS_TIME; info->name = "SF2OfsTime"; ipatch_unit_register(info); info->id = IPATCH_UNIT_TYPE_CENTIBELS; info->flags = IPATCH_UNIT_LOGARITHMIC; info->name = "Centibels"; ipatch_unit_register(info); info->flags = 0; info->id = IPATCH_UNIT_TYPE_32K_SAMPLES; info->name = "32kSamples"; ipatch_unit_register(info); info->id = IPATCH_UNIT_TYPE_TENTH_PERCENT; info->name = "TenthPercent"; ipatch_unit_register(info); ipatch_unit_info_free(info); /* done with info structure, free it */ /* SF2 absolute pitch <==> DLS absolute pitch */ ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_SF2_ABS_PITCH, IPATCH_UNIT_TYPE_DLS_ABS_PITCH, ipatch_unit_sf2_abs_pitch_to_dls_abs_pitch_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_DLS_ABS_PITCH, IPATCH_UNIT_TYPE_SF2_ABS_PITCH, ipatch_unit_dls_abs_pitch_to_sf2_abs_pitch_value); /* SF2 absolute pitch <==> Hertz */ ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_SF2_ABS_PITCH, IPATCH_UNIT_TYPE_HERTZ, ipatch_unit_sf2_abs_pitch_to_hertz_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_HERTZ, IPATCH_UNIT_TYPE_SF2_ABS_PITCH, ipatch_unit_hertz_to_sf2_abs_pitch_value); /* SF2 offset pitch <==> multiplier (reuse ABS time to seconds - same equation) */ ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_SF2_OFS_PITCH, IPATCH_UNIT_TYPE_MULTIPLIER, ipatch_unit_sf2_abs_time_to_seconds_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_MULTIPLIER, IPATCH_UNIT_TYPE_SF2_OFS_PITCH, ipatch_unit_seconds_to_sf2_abs_time_value); /* SF2 absolute time <==> DLS absolute time */ ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_SF2_ABS_TIME, IPATCH_UNIT_TYPE_DLS_ABS_TIME, ipatch_unit_sf2_abs_time_to_dls_abs_time_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_DLS_ABS_TIME, IPATCH_UNIT_TYPE_SF2_ABS_TIME, ipatch_unit_dls_abs_time_to_sf2_abs_time_value); /* SF2 absolute time <==> Seconds */ ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_SF2_ABS_TIME, IPATCH_UNIT_TYPE_SECONDS, ipatch_unit_sf2_abs_time_to_seconds_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_SECONDS, IPATCH_UNIT_TYPE_SF2_ABS_TIME, ipatch_unit_seconds_to_sf2_abs_time_value); /* SF2 offset time <==> multiplier (reuse ABS time to seconds - same equation) */ ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_SF2_OFS_TIME, IPATCH_UNIT_TYPE_MULTIPLIER, ipatch_unit_sf2_abs_time_to_seconds_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_MULTIPLIER, IPATCH_UNIT_TYPE_SF2_OFS_TIME, ipatch_unit_seconds_to_sf2_abs_time_value); /* Centibels <==> DLS gain */ ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_CENTIBELS, IPATCH_UNIT_TYPE_DLS_GAIN, ipatch_unit_centibels_to_dls_gain_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_DLS_GAIN, IPATCH_UNIT_TYPE_CENTIBELS, ipatch_unit_dls_gain_to_centibels_value); /* Centibels <==> Decibels */ ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_CENTIBELS, IPATCH_UNIT_TYPE_DECIBELS, ipatch_unit_centibels_to_decibels_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_DECIBELS, IPATCH_UNIT_TYPE_CENTIBELS, ipatch_unit_decibels_to_centibels_value); /* TenthPercent <==> Percent */ ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_TENTH_PERCENT, IPATCH_UNIT_TYPE_PERCENT, ipatch_unit_tenth_percent_to_percent_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_PERCENT, IPATCH_UNIT_TYPE_TENTH_PERCENT, ipatch_unit_percent_to_tenth_percent_value); /* Register converter for IPATCH_UNIT_TYPE_SEMITONES and IPATCH_UNIT_TYPE_CENTS. These mapping must be registered in the unit domain IPATCH_UNIT_CLASS_USER. */ /* semitones => semitones */ ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_USER, IPATCH_UNIT_TYPE_SEMITONES, IPATCH_UNIT_TYPE_SEMITONES); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_SEMITONES, IPATCH_UNIT_TYPE_SEMITONES, NULL); /* Cents => Cents */ ipatch_unit_class_register_map (IPATCH_UNIT_CLASS_USER, IPATCH_UNIT_TYPE_CENTS, IPATCH_UNIT_TYPE_CENTS); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_CENTS, IPATCH_UNIT_TYPE_CENTS, NULL); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_USER, IPATCH_UNIT_TYPE_SF2_ABS_PITCH, IPATCH_UNIT_TYPE_HERTZ); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_DLS, IPATCH_UNIT_TYPE_SF2_ABS_PITCH, IPATCH_UNIT_TYPE_DLS_ABS_PITCH); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_USER, IPATCH_UNIT_TYPE_SF2_OFS_PITCH, IPATCH_UNIT_TYPE_MULTIPLIER); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_USER, IPATCH_UNIT_TYPE_SF2_ABS_TIME, IPATCH_UNIT_TYPE_SECONDS); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_DLS, IPATCH_UNIT_TYPE_SF2_ABS_TIME, IPATCH_UNIT_TYPE_DLS_ABS_TIME); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_USER, IPATCH_UNIT_TYPE_SF2_OFS_TIME, IPATCH_UNIT_TYPE_MULTIPLIER); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_USER, IPATCH_UNIT_TYPE_CENTIBELS, IPATCH_UNIT_TYPE_DECIBELS); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_DLS, IPATCH_UNIT_TYPE_CENTIBELS, IPATCH_UNIT_TYPE_DLS_GAIN); ipatch_unit_class_register_map(IPATCH_UNIT_CLASS_USER, IPATCH_UNIT_TYPE_TENTH_PERCENT, IPATCH_UNIT_TYPE_PERCENT); } /** * ipatch_unit_sf2_abs_pitch_to_dls_abs_pitch: * @sf2_abs_pitch: Value in SF2 absolute pitch * * Converts a value from SF2 absolute pitch to DLS absolute pitch. * * sf2_abs_pitch = 1200 * log2(f/8.176) * f = 8.176 * 2^(sf2_abs_pitch/1200) * * dls_abs_pitch = (1200 * log2(f/440) + 6900) * 65536 * f = 440 * 2^((dls_abs_pitch / 65536 - 6900) / 1200) * * Returns: Value converted to DLS absolute pitch */ int ipatch_unit_sf2_abs_pitch_to_dls_abs_pitch(int sf2_abs_pitch) { double hz; hz = 8.176 * pow(2.0, ((double)sf2_abs_pitch) / 1200.0); return (int)((1200.0 * (log(hz / 440.0) / log(2.0)) + 6900.0) * 65536.0 + 0.5); /* +0.5 for rounding */ } /** * ipatch_unit_dls_abs_pitch_to_sf2_abs_pitch: * @dls_abs_pitch: Value in DLS absolute pitch * * Converts a value from DLS absolute pitch to SF2 absolute pitch. * See ipatch_unit_sf2_abs_pitch_to_dls_abs_pitch() * * Returns: Value converted to SF2 absolute pitch */ int ipatch_unit_dls_abs_pitch_to_sf2_abs_pitch(int dls_abs_pitch) { double hz; hz = 440.0 * pow(2.0, (((double)dls_abs_pitch / 65536.0 - 6900.0) / 1200.0)); return (int)(1200.0 * (log(hz / 8.176) / log(2.0)) + 0.5); /* +0.5 to round */ } /** * ipatch_unit_sf2_abs_pitch_to_hertz: * @sf2_abs_pitch: Value in SoundFont absolute pitch * * Convert SoundFont absolute pitch to frequency in Hertz. * * Returns: Value in Hertz (cycles per second) */ double ipatch_unit_sf2_abs_pitch_to_hertz(int sf2_abs_pitch) { return (8.176 * pow(2.0, ((double)sf2_abs_pitch) / 1200.0)); } /** * ipatch_unit_hertz_to_sf2_abs_pitch: * @hz: Hertz (cycles per second) value * * Convert frequency in Hertz to SoundFont absolute pitch. * * Returns: Converted value in SoundFont absolute pitch. */ int ipatch_unit_hertz_to_sf2_abs_pitch(double hz) { return (int)(log(hz / 8.176) / log(2) * 1200 + 0.5); /* +0.5 for rounding */ } /** * ipatch_unit_sf2_ofs_pitch_to_multiplier: * @sf2_ofs_pitch: Value in SoundFont offset pitch * * Convert SoundFont offset pitch (cents) to multiplier factor. * * Returns: Multiplier factor */ double ipatch_unit_sf2_ofs_pitch_to_multiplier(int sf2_ofs_pitch) { return (pow(2.0, ((double)sf2_ofs_pitch) / 1200.0)); } /** * ipatch_unit_multiplier_to_sf2_ofs_pitch: * @multiplier: Multiplier factor * * Convert multiplier factor to SoundFont offset pitch (cents). * * Returns: Converted value in SoundFont offset pitch (cents). */ int ipatch_unit_multiplier_to_sf2_ofs_pitch(double multiplier) { return (int)(log(multiplier) / log(2) * 1200 + 0.5); /* +0.5 for rounding */ } /** * ipatch_unit_sf2_abs_time_to_dls_abs_time: * @sf2_abs_time: Value in SF2 absolute time (timecents) * * Convert a value from SF2 absolute time to DLS absolute time. * * sf2_abs_time = 1200 * log2 (seconds) * seconds = 2^(sf2_abs_time / 1200) * * dls_abs_time = 1200 * log2 (seconds) * 65536 * seconds = 2^(dls_abs_time / (1200 * 65536)) * * Returns: Value converted to DLS absolute time. */ int ipatch_unit_sf2_abs_time_to_dls_abs_time(int sf2_abs_time) { return (sf2_abs_time * 65536); } /** * ipatch_unit_dls_abs_time_to_sf2_abs_time: * @dls_abs_time: Value in DLS absolute time * * Convert a value from DLS absolute time to SF2 absolute time. * See ipatch_unit_sf2_abs_time_to_dls_abs_time() * * Returns: Value converted to SF2 absolute time */ int ipatch_unit_dls_abs_time_to_sf2_abs_time(int dls_abs_time) { return ((dls_abs_time + 32768) / 65536); /* +32768 for rounding */ } /** * ipatch_unit_sf2_abs_time_to_seconds: * @sf2_abs_time: Value in SoundFont absolute time * * Convert a value from SoundFont absolute time (timecents) to seconds. * * Returns: Value in seconds */ double ipatch_unit_sf2_abs_time_to_seconds(int sf2_abs_time) { return (pow(2.0, (double)sf2_abs_time / 1200.0)); } /** * ipatch_unit_seconds_to_sf2_abs_time: * @sec: Value in seconds * * Convert value from seconds to SoundFont absolute time (timecents). * * Returns: Value in SoundFont absolute time */ int ipatch_unit_seconds_to_sf2_abs_time(double sec) { return (int)(log(sec) / log(2) * 1200 + 0.5); /* +0.5 for rounding */ } /** * ipatch_unit_sf2_ofs_time_to_multiplier: * @sf2_ofs_time: Value in SoundFont offset time (timecents) * * Convert a value from SoundFont offset time (timecents) to a multiplier. * * Returns: Multiplier factor */ double ipatch_unit_sf2_ofs_time_to_multiplier(int sf2_ofs_time) { return (pow(2.0, (double)sf2_ofs_time / 1200.0)); } /** * ipatch_unit_multiplier_to_sf2_ofs_time: * @multiplier: Multiplier factor * * Convert value from a multiplier to SoundFont offset time (timecents). * * Returns: Value in SoundFont offset time (timecents) */ int ipatch_unit_multiplier_to_sf2_ofs_time(double multiplier) { return (int)(log(multiplier) / log(2) * 1200 + 0.5); /* +0.5 for rounding */ } /** * ipatch_unit_centibels_to_dls_gain: * @centibel: Value in centibels (10th of a Decibel) * * Convert a value from centibels to DLS gain (1/655360th of a dB). * * V = target amplitude, v = original amplitude * centibel = 200 * log10 (v / V) * dls_gain = 200 * 65536 * log10 (V / v) * * Returns: Value converted to DLS gain */ int ipatch_unit_centibels_to_dls_gain(int centibel) { return (centibel * 65536); } /** * ipatch_unit_dls_gain_to_centibels: * @dls_gain: Value in DLS gain (1/655360th of a dB) * * Convert a value from DLS gain to centibels. * * Returns: Value converted to centibels. */ int ipatch_unit_dls_gain_to_centibels(int dls_gain) { return ((dls_gain + 32768) / 65536); /* +32768 for rounding */ } /** * ipatch_unit_centibels_to_decibels: * @cb: Value in Centibels (10th of a Decibel) * * Convert a value from Centibels to Decibels. * * Returns: Value in Decibels */ double ipatch_unit_centibels_to_decibels(int cb) { return ((double) cb / 10.0); } /** * ipatch_unit_decibels_to_centibels: * @db: Value in Decibels * * Convert Decibels to Centibels (10ths of a dB) * * Returns: Converted value in Centibels (10ths of a Decibel) */ int ipatch_unit_decibels_to_centibels(double db) { return (int)(db * 10.0 + 0.5); /* +0.5 for rounding */ } /** * ipatch_unit_tenth_percent_to_percent: * @tenth_percent: Value in 10ths of a Percent (Percent * 10) * * Convert a value from 10ths of a Percent to Percent. * * Returns: Value in Percent */ double ipatch_unit_tenth_percent_to_percent(int tenth_percent) { return ((double) tenth_percent / 10.0); } /** * ipatch_unit_percent_to_tenth_percent: * @percent: Value in Percent * * Convert Percent to 10ths of a Percent (Percent * 10) * * Returns: Converted value in 10ths of a Percent */ int ipatch_unit_percent_to_tenth_percent(double percent) { return (int)(percent * 10.0 + 0.5); /* +0.5 for rounding */ } /* ================================================= GValue conversion functions, duplicated for speed ================================================= */ static void ipatch_unit_sf2_abs_pitch_to_dls_abs_pitch_value(const GValue *src_val, GValue *dest_val) { int sf2_abs_pitch = g_value_get_int(src_val); int dls_abs_pitch; double hz; hz = 8.176 * pow(2.0, ((double)sf2_abs_pitch) / 1200.0); dls_abs_pitch = (int)((1200.0 * (log(hz / 440.0) / log(2.0)) + 6900.0) * 65536.0 + 0.5); g_value_set_int(dest_val, dls_abs_pitch); } static void ipatch_unit_dls_abs_pitch_to_sf2_abs_pitch_value(const GValue *src_val, GValue *dest_val) { int dls_abs_pitch = g_value_get_int(src_val); int sf2_abs_pitch; double hz; hz = 440.0 * pow(2.0, (((double)dls_abs_pitch / 65536.0 - 6900.0) / 1200.0)); sf2_abs_pitch = (int)(1200.0 * (log(hz / 8.176) / log(2.0)) + 0.5); g_value_set_int(dest_val, sf2_abs_pitch); } static void ipatch_unit_sf2_abs_pitch_to_hertz_value(const GValue *src_val, GValue *dest_val) { int sf2_abs_pitch = g_value_get_int(src_val); g_value_set_double(dest_val, 8.176 * pow(2.0, ((double) sf2_abs_pitch) / 1200.0)); } static void ipatch_unit_hertz_to_sf2_abs_pitch_value(const GValue *src_val, GValue *dest_val) { double hz = g_value_get_double(src_val); g_value_set_int(dest_val, (gint)(log(hz / 8.176) / log(2) * 1200 + 0.5)); } static void ipatch_unit_sf2_abs_time_to_dls_abs_time_value(const GValue *src_val, GValue *dest_val) { int sf2_abs_time = g_value_get_int(src_val); g_value_set_int(dest_val, sf2_abs_time * 65536); } static void ipatch_unit_dls_abs_time_to_sf2_abs_time_value(const GValue *src_val, GValue *dest_val) { int dls_abs_time = g_value_get_int(src_val); g_value_set_int(dest_val, (dls_abs_time + 32768) / 65536); } static void ipatch_unit_sf2_abs_time_to_seconds_value(const GValue *src_val, GValue *dest_val) { int sf2_abs_time = g_value_get_int(src_val); g_value_set_double(dest_val, pow(2.0, (double)sf2_abs_time / 1200.0)); } static void ipatch_unit_seconds_to_sf2_abs_time_value(const GValue *src_val, GValue *dest_val) { double sec = g_value_get_double(src_val); g_value_set_int(dest_val, (gint)(log(sec) / log(2) * 1200 + 0.5)); } static void ipatch_unit_centibels_to_dls_gain_value(const GValue *src_val, GValue *dest_val) { int centibels = g_value_get_int(src_val); g_value_set_int(dest_val, centibels * 65536); } static void ipatch_unit_dls_gain_to_centibels_value(const GValue *src_val, GValue *dest_val) { int dls_gain = g_value_get_int(src_val); g_value_set_int(dest_val, (dls_gain + 32768) / 65536); } static void ipatch_unit_centibels_to_decibels_value(const GValue *src_val, GValue *dest_val) { int cb = g_value_get_int(src_val); g_value_set_double(dest_val, (double)cb / 10.0); } static void ipatch_unit_decibels_to_centibels_value(const GValue *src_val, GValue *dest_val) { double db = g_value_get_double(src_val); g_value_set_int(dest_val, (gint)(db * 10.0 + 0.5)); } static void ipatch_unit_tenth_percent_to_percent_value(const GValue *src_val, GValue *dest_val) { int tenthperc = g_value_get_int(src_val); g_value_set_double(dest_val, (double)tenthperc / 10.0); } static void ipatch_unit_percent_to_tenth_percent_value(const GValue *src_val, GValue *dest_val) { double percent = g_value_get_double(src_val); g_value_set_int(dest_val, (gint)(percent * 10.0 + 0.5)); } libinstpatch-1.1.6/libinstpatch/IpatchUnit_SF2.h000066400000000000000000000037551400263525300215740ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_UNIT_SF2_H__ #define __IPATCH_UNIT_SF2_H__ #include #include int ipatch_unit_sf2_abs_pitch_to_dls_abs_pitch(int sf2_abs_pitch); int ipatch_unit_dls_abs_pitch_to_sf2_abs_pitch(int dls_abs_pitch); double ipatch_unit_sf2_abs_pitch_to_hertz(int sf2_abs_pitch); int ipatch_unit_hertz_to_sf2_abs_pitch(double hz); double ipatch_unit_sf2_ofs_pitch_to_multiplier(int sf2_ofs_pitch); int ipatch_unit_multiplier_to_sf2_ofs_pitch(double multiplier); int ipatch_unit_sf2_abs_time_to_dls_abs_time(int sf2_abs_time); int ipatch_unit_dls_abs_time_to_sf2_abs_time(int dls_abs_time); double ipatch_unit_sf2_abs_time_to_seconds(int sf2_abs_time); int ipatch_unit_seconds_to_sf2_abs_time(double sec); double ipatch_unit_sf2_ofs_time_to_multiplier(int sf2_ofs_time); int ipatch_unit_multiplier_to_sf2_ofs_time(double multiplier); int ipatch_unit_centibels_to_dls_gain(int centibel); int ipatch_unit_dls_gain_to_centibels(int dls_gain); double ipatch_unit_centibels_to_decibels(int cb); int ipatch_unit_decibels_to_centibels(double db); double ipatch_unit_tenth_percent_to_percent(int tenth_percent); int ipatch_unit_percent_to_tenth_percent(double percent); #endif libinstpatch-1.1.6/libinstpatch/IpatchUnit_generic.c000066400000000000000000000150721400263525300226040ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchUnit_generic * @short_description: Generic unit types and conversions * @see_also: * @stability: Stable */ #include #include #include #include #include "IpatchUnit_generic.h" #include "IpatchUnit.h" #include "IpatchRange.h" #include "i18n.h" /* used to convert cents (100ths of a semitone) to hertz */ #define HERTZ_CENTS_FACTOR 8.175798915643707 static void ipatch_unit_cents_to_hertz_value(const GValue *src_val, GValue *dest_val); static void ipatch_unit_hertz_to_cents_value(const GValue *src_val, GValue *dest_val); /** * _ipatch_unit_generic_init: (skip) */ void _ipatch_unit_generic_init(void) { IpatchUnitInfo *info; /* register generic unit types */ info = ipatch_unit_info_new(); info->name = "Int"; info->id = IPATCH_UNIT_TYPE_INT; info->label = NULL; info->descr = NULL; info->value_type = G_TYPE_INT; info->flags = 0; info->digits = 0; ipatch_unit_register(info); info->name = "UInt"; info->id = IPATCH_UNIT_TYPE_UINT; info->value_type = G_TYPE_UINT; ipatch_unit_register(info); info->name = "Range"; info->id = IPATCH_UNIT_TYPE_RANGE; info->value_type = IPATCH_TYPE_RANGE; ipatch_unit_register(info); info->name = "Decibels"; info->id = IPATCH_UNIT_TYPE_DECIBELS; info->label = _("dB"); /* abbreviation for decibel */ info->descr = _("Decibels"); info->value_type = G_TYPE_DOUBLE; info->flags = IPATCH_UNIT_LOGARITHMIC | IPATCH_UNIT_USER; info->digits = 3; ipatch_unit_register(info); info->name = "Percent"; info->id = IPATCH_UNIT_TYPE_PERCENT; info->label = _("%"); /* abbreviation for percent */ info->descr = _("Percent"); info->value_type = G_TYPE_DOUBLE; info->flags = IPATCH_UNIT_USER; info->digits = 1; ipatch_unit_register(info); info->name = "Semitones"; info->id = IPATCH_UNIT_TYPE_SEMITONES; info->label = _("Notes"); info->descr = _("Unit of pitch ratio (one note)"); info->value_type = G_TYPE_DOUBLE; info->flags = IPATCH_UNIT_USER; info->digits = 0; ipatch_unit_register(info); info->name = "Cents"; info->id = IPATCH_UNIT_TYPE_CENTS; info->label = _("Cents"); info->descr = _("Unit of pitch ratio (100th of a semitone)"); info->value_type = G_TYPE_DOUBLE; info->flags = IPATCH_UNIT_USER; info->digits = 0; ipatch_unit_register(info); info->name = "TimeCents"; info->id = IPATCH_UNIT_TYPE_TIME_CENTS; info->label = _("T-Cents"); info->descr = _("Time ratio in cents (1200 cents = 2x)"); info->value_type = G_TYPE_DOUBLE; info->flags = IPATCH_UNIT_USER; info->digits = 3; ipatch_unit_register(info); info->name = "SampleRate"; info->id = IPATCH_UNIT_TYPE_SAMPLE_RATE; info->label = _("Rate"); /* abbreviation for sample rate */ info->descr = _("Audio sampling rate"); info->value_type = G_TYPE_DOUBLE; info->flags = IPATCH_UNIT_USER; info->digits = 0; ipatch_unit_register(info); info->name = "Samples"; info->id = IPATCH_UNIT_TYPE_SAMPLES; info->label = _("Samples"); info->descr = _("Number of sample points"); info->value_type = G_TYPE_INT; info->flags = IPATCH_UNIT_USER; info->digits = 0; ipatch_unit_register(info); info->name = "Hertz"; info->id = IPATCH_UNIT_TYPE_HERTZ; info->label = _("Hz"); /* abbreviation for Hertz */ info->descr = _("Frequency in Hertz (cycles per second)"); info->value_type = G_TYPE_DOUBLE; info->flags = IPATCH_UNIT_USER; info->digits = 3; ipatch_unit_register(info); info->name = "Seconds"; info->id = IPATCH_UNIT_TYPE_SECONDS; info->label = _("Sec"); /* abbreviation for seconds */ info->descr = _("Amount of time in seconds"); info->value_type = G_TYPE_DOUBLE; info->flags = IPATCH_UNIT_USER; info->digits = 3; ipatch_unit_register(info); info->name = "Multiplier"; info->id = IPATCH_UNIT_TYPE_MULTIPLIER; info->label = _("X"); /* abbreviation for multiplier */ info->descr = _("Multiplier"); info->value_type = G_TYPE_DOUBLE; info->flags = IPATCH_UNIT_USER; info->digits = 3; ipatch_unit_register(info); ipatch_unit_info_free(info); /* done with unit info structure, free it */ /* conversion functions */ /* Hertz <==> Cents */ ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_CENTS, IPATCH_UNIT_TYPE_HERTZ, ipatch_unit_cents_to_hertz_value); ipatch_unit_conversion_register (IPATCH_UNIT_TYPE_HERTZ, IPATCH_UNIT_TYPE_CENTS, ipatch_unit_hertz_to_cents_value); } /** * ipatch_unit_cents_to_hertz: * @cents: Value in cents * * Convert cents to relative frequency in Hertz. * * Returns: Value in relative Hertz (cycles per second) */ double ipatch_unit_cents_to_hertz(double cents) { return (HERTZ_CENTS_FACTOR * pow(2.0, cents / 1200.0)); } /** * ipatch_unit_hertz_to_cents: * @hz: Hertz (cycles per second) value * * Convert frequency in Hertz to relative cents. * * Returns: Converted value in relative cents. */ double ipatch_unit_hertz_to_cents(double hz) { return (log(hz / HERTZ_CENTS_FACTOR) / log(2) * 1200); } /* ================================================= GValue conversion functions, duplicated for speed ================================================= */ static void ipatch_unit_cents_to_hertz_value(const GValue *src_val, GValue *dest_val) { double cents = g_value_get_double(src_val); g_value_set_double(dest_val, HERTZ_CENTS_FACTOR * pow(2.0, cents / 1200.0)); } static void ipatch_unit_hertz_to_cents_value(const GValue *src_val, GValue *dest_val) { double hz = g_value_get_double(src_val); g_value_set_double(dest_val, log(hz / HERTZ_CENTS_FACTOR) / log(2) * 1200); } libinstpatch-1.1.6/libinstpatch/IpatchUnit_generic.h000066400000000000000000000020341400263525300226030ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_UNIT_GENERIC_H__ #define __IPATCH_UNIT_GENERIC_H__ #include #include double ipatch_unit_hertz_to_cents(double hz); double ipatch_unit_cents_to_hertz(double cents); #endif libinstpatch-1.1.6/libinstpatch/IpatchVBank.c000066400000000000000000000427771400263525300212060ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchVBank * @short_description: Virtual bank object * @see_also: * @stability: Stable * * Virtual banks provide the capability of creating new instrument MIDI * maps from components from other files of possibly different types. */ #include #include #include #include #include #include "IpatchVBank.h" #include "IpatchParamProp.h" #include "i18n.h" #include "misc.h" /* !! Keep synchronized with IPATCH_VBANK_INFO_COUNT constant in IpatchVBank.h */ enum { PROP_0, PROP_PARSER_VERSION, /* IVBank version of parser which wrote the file */ PROP_REQUIRE_VERSION, /* IVBank parser version required */ PROP_ENGINE, /* Sound engine "FluidSynth 1.0.x" for example */ PROP_NAME, /* Descriptive name of bank */ PROP_DATE, /* Creation date */ PROP_AUTHOR, /* Author */ PROP_COMMENT /* Comments */ }; #define IPATCH_VBANK_PARSER_VERSION "1.0" // Current IVBank parser version. static void ipatch_vbank_finalize(GObject *gobject); static void ipatch_vbank_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_vbank_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_vbank_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static const GType *ipatch_vbank_container_child_types(void); static gboolean ipatch_vbank_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type); static void ipatch_vbank_container_make_unique(IpatchContainer *container, IpatchItem *item); static void ipatch_vbank_base_find_unused_locale(IpatchBase *base, int *bank, int *program, const IpatchItem *exclude, gboolean percussion); static int locale_gcompare_func(gconstpointer a, gconstpointer b); static IpatchItem * ipatch_vbank_base_find_item_by_locale(IpatchBase *base, int bank, int program); G_DEFINE_TYPE(IpatchVBank, ipatch_vbank, IPATCH_TYPE_BASE) static GType vbank_child_types[2] = { 0 }; static void ipatch_vbank_class_init(IpatchVBankClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass); IpatchBaseClass *base_class = IPATCH_BASE_CLASS(klass); obj_class->finalize = ipatch_vbank_finalize; obj_class->get_property = ipatch_vbank_get_property; /* we use the IpatchItem item_set_property method */ item_class->item_set_property = ipatch_vbank_set_property; item_class->copy = ipatch_vbank_item_copy; container_class->child_types = ipatch_vbank_container_child_types; container_class->init_iter = ipatch_vbank_container_init_iter; container_class->make_unique = ipatch_vbank_container_make_unique; base_class->find_unused_locale = ipatch_vbank_base_find_unused_locale; base_class->find_item_by_locale = ipatch_vbank_base_find_item_by_locale; g_object_class_override_property(obj_class, PROP_NAME, "title"); g_object_class_install_property(obj_class, PROP_PARSER_VERSION, g_param_spec_string("parser-version", _("Parser version"), _("Parser version"), IPATCH_VBANK_PARSER_VERSION, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_REQUIRE_VERSION, g_param_spec_string("require-version", _("Require version"), _("Required parser version"), IPATCH_VBANK_PARSER_VERSION, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_ENGINE, ipatch_param_set(g_param_spec_string("engine", _("Engine"), _("Synthesis engine"), NULL, G_PARAM_READWRITE), "string-max-length", 255, NULL)); g_object_class_install_property(obj_class, PROP_NAME, ipatch_param_set(g_param_spec_string("name", _("Name"), _("Descriptive name"), NULL, G_PARAM_READWRITE), "string-max-length", 255, NULL)); g_object_class_install_property(obj_class, PROP_DATE, ipatch_param_set(g_param_spec_string("date", _("Date"), _("Creation date"), NULL, G_PARAM_READWRITE), "string-max-length", 255, NULL)); g_object_class_install_property(obj_class, PROP_AUTHOR, ipatch_param_set(g_param_spec_string("author", _("Author"), _("Author of file"), NULL, G_PARAM_READWRITE), "string-max-length", 255, NULL)); g_object_class_install_property(obj_class, PROP_COMMENT, ipatch_param_set(g_param_spec_string("comment", _("Comments"), _("Comments"), NULL, G_PARAM_READWRITE), "string-max-length", 65535, NULL)); vbank_child_types[0] = IPATCH_TYPE_VBANK_INST; } static void ipatch_vbank_init(IpatchVBank *vbank) { g_object_set(vbank, "name", _(IPATCH_BASE_DEFAULT_NAME), NULL); ipatch_item_clear_flags(IPATCH_ITEM(vbank), IPATCH_BASE_CHANGED); } /* function called when VBank is being destroyed */ static void ipatch_vbank_finalize(GObject *gobject) { IpatchVBank *vbank = IPATCH_VBANK(gobject); int i; IPATCH_ITEM_WLOCK(vbank); for(i = 0; i < IPATCH_VBANK_INFO_COUNT; i++) { g_free(vbank->info[i]); } IPATCH_ITEM_WUNLOCK(vbank); if(G_OBJECT_CLASS(ipatch_vbank_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_vbank_parent_class)->finalize(gobject); } } static void ipatch_vbank_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchVBank *vbank = IPATCH_VBANK(object); if(property_id > PROP_0 && property_id <= IPATCH_VBANK_INFO_COUNT) { g_free(vbank->info[property_id - 1]); vbank->info[property_id - 1] = g_value_dup_string(value); /* need to do a title property notify? */ if(property_id == PROP_NAME) ipatch_item_prop_notify((IpatchItem *)vbank, ipatch_item_pspec_title, value, NULL); } else { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } static void ipatch_vbank_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchVBank *vbank = IPATCH_VBANK(object); if(property_id > PROP_0 && property_id <= IPATCH_VBANK_INFO_COUNT) { g_value_set_string(value, vbank->info[property_id - 1]); } else { G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); } } /* item copy function, note that this is an #IpatchBase derived object, so link_func is not used */ static void ipatch_vbank_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchVBank *src_vbank, *dest_vbank; IpatchItem *newitem; GSList *p; int i; src_vbank = IPATCH_VBANK(src); dest_vbank = IPATCH_VBANK(dest); IPATCH_ITEM_RLOCK(src_vbank); if(IPATCH_BASE(src_vbank)->file) ipatch_base_set_file(IPATCH_BASE(dest_vbank), IPATCH_BASE(src_vbank)->file); /* duplicate the info variables */ for(i = 0; i < IPATCH_VBANK_INFO_COUNT; i++) { dest_vbank->info[i] = g_strdup(src_vbank->info[i]); } /* duplicate instruments */ for(p = src_vbank->insts; p; p = p->next) { /* ++ ref new duplicate instrument, !! inst list takes it over */ newitem = ipatch_item_duplicate((IpatchItem *)(p->data)); dest_vbank->insts = g_slist_prepend(dest_vbank->insts, newitem); ipatch_item_set_parent(newitem, IPATCH_ITEM(dest_vbank)); } IPATCH_ITEM_RUNLOCK(src_vbank); dest_vbank->insts = g_slist_reverse(dest_vbank->insts); } static const GType * ipatch_vbank_container_child_types(void) { return (vbank_child_types); } /* container is locked by caller */ static gboolean ipatch_vbank_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type) { IpatchVBank *vbank = IPATCH_VBANK(container); if(g_type_is_a(type, IPATCH_TYPE_VBANK_INST)) { ipatch_iter_GSList_init(iter, &vbank->insts); } else { g_critical("Invalid child type '%s' for parent of type '%s'", g_type_name(type), g_type_name(G_OBJECT_TYPE(container))); return (FALSE); } return (TRUE); } static void ipatch_vbank_container_make_unique(IpatchContainer *container, IpatchItem *item) { IpatchVBank *vbank = IPATCH_VBANK(container); char *name, *newname; IPATCH_ITEM_WLOCK(vbank); if(IPATCH_IS_VBANK_INST(item)) { int bank, newbank, program, newprogram; ipatch_vbank_inst_get_midi_locale(IPATCH_VBANK_INST(item), &bank, &program); newbank = bank; newprogram = program; ipatch_base_find_unused_midi_locale(IPATCH_BASE(vbank), &newbank, &newprogram, item, FALSE); if(bank != newbank || program != newprogram) ipatch_vbank_inst_set_midi_locale(IPATCH_VBANK_INST(item), newbank, newprogram); } else { g_critical("Invalid child type '%s' for IpatchVBank object", g_type_name(G_TYPE_FROM_INSTANCE(item))); return; } g_object_get(item, "name", &name, NULL); newname = ipatch_vbank_make_unique_name(vbank, name, NULL); if(!name || strcmp(name, newname) != 0) { g_object_set(item, "name", newname, NULL); } IPATCH_ITEM_WUNLOCK(vbank); g_free(name); g_free(newname); } /* base method to find an unused MIDI bank:program locale */ static void ipatch_vbank_base_find_unused_locale(IpatchBase *base, int *bank, int *program, const IpatchItem *exclude, gboolean percussion) { IpatchVBank *vbank = IPATCH_VBANK(base); GSList *locale_list = NULL; IpatchVBankInst *inst; GSList *p; guint b, n; /* Stores current bank and program number */ guint lbank, lprogram; /* fill array with bank and program numbers */ IPATCH_ITEM_RLOCK(vbank); for(p = vbank->insts; p; p = p->next) { inst = (IpatchVBankInst *)(p->data); /* only add to locale list if not the exclude item */ if((gpointer)inst != (gpointer)exclude) locale_list = g_slist_prepend(locale_list, GUINT_TO_POINTER (((guint32)inst->bank << 16) | inst->program)); } IPATCH_ITEM_RUNLOCK(vbank); if(!locale_list) { return; } locale_list = g_slist_sort(locale_list, (GCompareFunc)locale_gcompare_func); b = *bank; n = *program; /* loop through sorted list of bank:programs */ p = locale_list; while(p) { lprogram = GPOINTER_TO_UINT(p->data); lbank = lprogram >> 16; lprogram &= 0xFFFF; if(lbank > b || (lbank == b && lprogram > n)) { break; } if(lbank >= b) { if(++n > 127) { n = 0; b++; } } p = g_slist_delete_link(p, p); /* delete and advance */ } *bank = b; *program = n; if(p) { g_slist_free(p); /* free remainder of list */ } } /* function used to do a temporary sort on preset list for ipatch_vbank_base_find_unused_locale */ static int locale_gcompare_func(gconstpointer a, gconstpointer b) { return (GPOINTER_TO_UINT(a) - GPOINTER_TO_UINT(b)); } static IpatchItem * ipatch_vbank_base_find_item_by_locale(IpatchBase *base, int bank, int program) { IpatchVBankInst *inst; inst = ipatch_vbank_find_inst(IPATCH_VBANK(base), NULL, bank, program, NULL); return ((IpatchItem *)inst); } /** * ipatch_vbank_new: * * Create a new virtual bank base object. * * Returns: New IVBank base object with a reference count of 1. Caller * owns the reference and removing it will destroy the item. */ IpatchVBank * ipatch_vbank_new(void) { return (IPATCH_VBANK(g_object_new(IPATCH_TYPE_VBANK, NULL))); } /** * ipatch_vbank_find_inst: * @vbank: VBank to search in * @name: (nullable): Name of instrument to find or %NULL to match any name * @bank: MIDI bank number of instrument to search for or -1 to not search by * MIDI bank:program numbers * @program: MIDI program number of instrument to search for, only used * if @bank is 0-128 * @exclude: (nullable): An instrument to exclude from the search or %NULL * * Find an instrument by name or bank:preset MIDI numbers. If instrument @name * and @bank:@program are specified then match for either condition. * If an instrument is found its reference count is incremented before it * is returned. The caller is responsible for removing the reference * with g_object_unref() when finished with it. * * Returns: (transfer full): The matching instrument or %NULL if not found. Remember to unref * the item when finished with it. */ IpatchVBankInst * ipatch_vbank_find_inst(IpatchVBank *vbank, const char *name, int bank, int program, const IpatchVBankInst *exclude) { IpatchVBankInst *inst; gboolean bynum = FALSE; GSList *p; g_return_val_if_fail(IPATCH_IS_VBANK(vbank), NULL); /* if bank and program are valid, then search by number */ if(bank >= 0 && bank <= 128 && program >= 0 && program < 128) { bynum = TRUE; } IPATCH_ITEM_RLOCK(vbank); for(p = vbank->insts; p; p = p->next) { inst = (IpatchVBankInst *)(p->data); IPATCH_ITEM_RLOCK(inst); /* MT - Recursive LOCK */ if(inst != exclude /* if exclude is NULL it will never == pset */ && ((bynum && inst->bank == bank && inst->program == program) || (name && strcmp(inst->name, name) == 0))) { g_object_ref(inst); IPATCH_ITEM_RUNLOCK(inst); IPATCH_ITEM_RUNLOCK(vbank); return (inst); } IPATCH_ITEM_RUNLOCK(inst); } IPATCH_ITEM_RUNLOCK(vbank); return (NULL); } /* In theory there is still a chance of duplicates if another item's name is set to the generated unique one (by another thread) while in this routine */ /** * ipatch_vbank_make_unique_name: * @vbank: VBank item * @name: (nullable): An initial name to use or %NULL * @exclude: (nullable): An item to exclude from search or %NULL * * Generates a unique instrument name for @vbank. The @name * parameter is used as a base and is modified, by appending a number, to * make it unique (if necessary). The @exclude parameter is used to exclude * an existing @vbank instrument from the search. * * MT-Note: To ensure that an item is actually unique before being * added to a VBank object, ipatch_container_add_unique() should be * used. * * Returns: A new unique name which should be freed when finished with it. */ char * ipatch_vbank_make_unique_name(IpatchVBank *vbank, const char *name, const IpatchVBankInst *exclude) { char curname[IPATCH_VBANK_INST_NAME_SIZE + 1]; IpatchVBankInst *inst; int count = 2; GSList *p; g_return_val_if_fail(IPATCH_IS_VBANK(vbank), NULL); if(!name) { name = _("New Instrument"); } g_strlcpy(curname, name, sizeof(curname)); IPATCH_ITEM_RLOCK(vbank); /* check for duplicate */ for(p = vbank->insts; p; p = p->next) { inst = (IpatchVBankInst *)(p->data); IPATCH_ITEM_RLOCK(inst); /* MT - Recursive LOCK */ if(p->data != exclude && strcmp(inst->name, curname) == 0) { /* duplicate name */ IPATCH_ITEM_RUNLOCK(inst); ipatch_strconcat_num(name, count++, curname, sizeof(curname)); p = vbank->insts; /* start over */ continue; } IPATCH_ITEM_RUNLOCK(inst); } IPATCH_ITEM_RUNLOCK(vbank); return (g_strdup(curname)); } libinstpatch-1.1.6/libinstpatch/IpatchVBank.h000066400000000000000000000051271400263525300211770ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_VBANK_H__ #define __IPATCH_VBANK_H__ #include #include #include #include /* forward type declarations */ typedef struct _IpatchVBank IpatchVBank; typedef struct _IpatchVBankClass IpatchVBankClass; #include #define IPATCH_TYPE_VBANK (ipatch_vbank_get_type ()) #define IPATCH_VBANK(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_VBANK, \ IpatchVBank)) #define IPATCH_VBANK_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_VBANK, \ IpatchVBankClass)) #define IPATCH_IS_VBANK(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VBANK)) #define IPATCH_IS_VBANK_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_VBANK)) #define IPATCH_VBANK_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_VBANK, \ IpatchVBankClass)) /** * IPATCH_VBANK_INFO_COUNT: (skip) * * Count of info strings */ /* !! Keep synchronized with count of properties in IpatchVBank.c */ #define IPATCH_VBANK_INFO_COUNT 7 /* Virtual bank */ struct _IpatchVBank { IpatchBase parent_instance; /*< private >*/ char *info[IPATCH_VBANK_INFO_COUNT]; GSList *insts; }; struct _IpatchVBankClass { IpatchBaseClass parent_class; }; GType ipatch_vbank_get_type(void); IpatchVBank *ipatch_vbank_new(void); #define ipatch_vbank_get_insts(vbank) \ ipatch_container_get_children (IPATCH_CONTAINER (vbank), \ IPATCH_TYPE_VBANK_INST) IpatchVBankInst * ipatch_vbank_find_inst(IpatchVBank *vbank, const char *name, int bank, int program, const IpatchVBankInst *exclude); char * ipatch_vbank_make_unique_name(IpatchVBank *vbank, const char *name, const IpatchVBankInst *exclude); #endif libinstpatch-1.1.6/libinstpatch/IpatchVBankInst.c000066400000000000000000000311121400263525300220210ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchVBankInst * @short_description: VBank instrument item * @see_also: #IpatchVBank * @stability: Stable * * VBank instruments are children of #IpatchVBank objects and define individual * instruments mapped to MIDI bank/program numbers and which reference items * in other instrument files. */ #include #include #include #include "IpatchVBankInst.h" #include "IpatchVBankRegion.h" #include "IpatchParamProp.h" #include "IpatchTypeProp.h" #include "ipatch_priv.h" /* properties */ enum { PROP_0, PROP_TITLE, PROP_NAME, PROP_BANK, PROP_PROGRAM }; static void ipatch_vbank_inst_finalize(GObject *gobject); static void ipatch_vbank_inst_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_vbank_inst_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_vbank_inst_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data); static const GType *ipatch_vbank_inst_container_child_types(void); static gboolean ipatch_vbank_inst_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type); G_DEFINE_TYPE(IpatchVBankInst, ipatch_vbank_inst, IPATCH_TYPE_CONTAINER) static GType inst_child_types[2] = { 0 }; static GParamSpec *name_pspec, *bank_pspec, *program_pspec; static void ipatch_vbank_inst_class_init(IpatchVBankInstClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass); obj_class->finalize = ipatch_vbank_inst_finalize; obj_class->get_property = ipatch_vbank_inst_get_property; /* we use the IpatchItem item_set_property method */ item_class->item_set_property = ipatch_vbank_inst_set_property; item_class->copy = ipatch_vbank_inst_item_copy; container_class->child_types = ipatch_vbank_inst_container_child_types; container_class->init_iter = ipatch_vbank_inst_container_init_iter; g_object_class_override_property(obj_class, PROP_TITLE, "title"); name_pspec = ipatch_param_set(g_param_spec_string("name", _("Name"), _("Name"), NULL, G_PARAM_READWRITE | IPATCH_PARAM_UNIQUE), "string-max-length", IPATCH_VBANK_INST_NAME_SIZE, NULL); g_object_class_install_property(obj_class, PROP_NAME, name_pspec); /* bank/program are grouped unique (siblings with same bank/program are considered conflicting) */ bank_pspec = g_param_spec_int("bank", _("Bank"), _("MIDI bank number"), 0, 128, 0, G_PARAM_READWRITE | IPATCH_PARAM_UNIQUE); ipatch_param_set(bank_pspec, "unique-group-id", 1, NULL); g_object_class_install_property(obj_class, PROP_BANK, bank_pspec); program_pspec = g_param_spec_int("program", _("Program"), _("MIDI program number"), 0, 127, 0, G_PARAM_READWRITE | IPATCH_PARAM_UNIQUE); ipatch_param_set(program_pspec, "unique-group-id", 1, NULL); g_object_class_install_property(obj_class, PROP_PROGRAM, program_pspec); inst_child_types[0] = IPATCH_TYPE_VBANK_REGION; } static void ipatch_vbank_inst_init(IpatchVBankInst *inst) { } static void ipatch_vbank_inst_finalize(GObject *gobject) { IpatchVBankInst *inst = IPATCH_VBANK_INST(gobject); IPATCH_ITEM_WLOCK(inst); g_free(inst->name); inst->name = NULL; IPATCH_ITEM_WUNLOCK(inst); if(G_OBJECT_CLASS(ipatch_vbank_inst_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_vbank_inst_parent_class)->finalize(gobject); } } static void ipatch_vbank_inst_get_title(IpatchVBankInst *inst, GValue *value) { int bank, program; char *name, *s; g_object_get(inst, "bank", &bank, "program", &program, "name", &name, NULL); s = g_strdup_printf("%03d-%03d %s", bank, program, name); g_free(name); g_value_take_string(value, s); } static void ipatch_vbank_inst_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchVBankInst *inst = IPATCH_VBANK_INST(object); switch(property_id) { case PROP_NAME: IPATCH_ITEM_WLOCK(inst); g_free(inst->name); inst->name = g_value_dup_string(value); IPATCH_ITEM_WUNLOCK(inst); break; case PROP_BANK: inst->bank = g_value_get_int(value); break; case PROP_PROGRAM: inst->program = g_value_get_int(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); return; } /* need to do title notify? */ if(property_id == PROP_NAME || property_id == PROP_BANK || property_id == PROP_PROGRAM) { GValue titleval = { 0 }; g_value_init(&titleval, G_TYPE_STRING); ipatch_vbank_inst_get_title(inst, &titleval); ipatch_item_prop_notify((IpatchItem *)inst, ipatch_item_pspec_title, &titleval, NULL); g_value_unset(&titleval); } } static void ipatch_vbank_inst_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchVBankInst *inst = IPATCH_VBANK_INST(object); switch(property_id) { case PROP_TITLE: ipatch_vbank_inst_get_title(inst, value); break; case PROP_NAME: IPATCH_ITEM_RLOCK(inst); g_value_set_string(value, inst->name); IPATCH_ITEM_RUNLOCK(inst); break; case PROP_BANK: g_value_set_int(value, inst->bank); break; case PROP_PROGRAM: g_value_set_int(value, inst->program); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_vbank_inst_item_copy(IpatchItem *dest, IpatchItem *src, IpatchItemCopyLinkFunc link_func, gpointer user_data) { IpatchVBankInst *src_inst, *dest_inst; IpatchItem *region; GSList *p; src_inst = IPATCH_VBANK_INST(src); dest_inst = IPATCH_VBANK_INST(dest); IPATCH_ITEM_RLOCK(src_inst); dest_inst->name = g_strdup(src_inst->name); dest_inst->program = src_inst->program; dest_inst->bank = src_inst->bank; for(p = src_inst->regions; p; p = p->next) { region = ipatch_item_duplicate(IPATCH_ITEM(p->data)); dest_inst->regions = g_slist_prepend(dest_inst->regions, region); ipatch_item_set_parent(region, IPATCH_ITEM(dest_inst)); } IPATCH_ITEM_RUNLOCK(src_inst); dest_inst->regions = g_slist_reverse(dest_inst->regions); } static const GType * ipatch_vbank_inst_container_child_types(void) { return (inst_child_types); } /* container is locked by caller */ static gboolean ipatch_vbank_inst_container_init_iter(IpatchContainer *container, IpatchIter *iter, GType type) { IpatchVBankInst *inst = IPATCH_VBANK_INST(container); if(!g_type_is_a(type, IPATCH_TYPE_VBANK_REGION)) { g_critical("Invalid child type '%s' for parent of type '%s'", g_type_name(type), g_type_name(G_OBJECT_TYPE(container))); return (FALSE); } ipatch_iter_GSList_init(iter, &inst->regions); return (TRUE); } /** * ipatch_vbank_inst_new: * * Create a new virtual bank instrument object. * * Returns: New VBank instrument with a reference count of 1. Caller * owns the reference and removing it will destroy the item. */ IpatchVBankInst * ipatch_vbank_inst_new(void) { return (IPATCH_VBANK_INST(g_object_new(IPATCH_TYPE_VBANK_INST, NULL))); } /** * ipatch_vbank_inst_first: (skip) * @iter: Patch item iterator containing #IpatchVBankInst items * * Gets the first item in an instrument iterator. A convenience wrapper for * ipatch_iter_first(). * * Returns: The first instrument in @iter or %NULL if empty. */ IpatchVBankInst * ipatch_vbank_inst_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_VBANK_INST(obj)); } else { return (NULL); } } /** * ipatch_vbank_inst_next: (skip) * @iter: Patch item iterator containing #IpatchVBankInst items * * Gets the next item in an instrument iterator. A convenience wrapper for * ipatch_iter_next(). * * Returns: The next instrument in @iter or %NULL if at the end of the list. */ IpatchVBankInst * ipatch_vbank_inst_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_VBANK_INST(obj)); } else { return (NULL); } } /** * ipatch_vbank_inst_new_region: * @inst: VBank instrument * @item: Referenced item for new region * * A convenience function for creating a new virtual bank region, adding it * to @inst and setting the region's referenced item to @item. */ void ipatch_vbank_inst_new_region(IpatchVBankInst *inst, IpatchItem *item) { IpatchVBankRegion *region; g_return_if_fail(IPATCH_IS_VBANK_INST(inst)); g_return_if_fail(IPATCH_IS_ITEM(item)); region = ipatch_vbank_region_new(); /* ++ ref new region */ g_object_set(region, "link-item", item, NULL); ipatch_container_append(IPATCH_CONTAINER(inst), IPATCH_ITEM(region)); g_object_unref(region); /* -- unref vbank region */ } /** * ipatch_vbank_inst_set_midi_locale: * @inst: Virtual bank instrument to set MIDI locale of * @bank: MIDI bank number to assign to instrument * @program: MIDI program number to assign to instrument * * Sets the MIDI locale of an instrument (bank and program numbers). */ void ipatch_vbank_inst_set_midi_locale(IpatchVBankInst *inst, int bank, int program) { g_object_set(inst, "bank", bank, "program", program, NULL); } /** * ipatch_vbank_inst_get_midi_locale: * @inst: Virtual bank instrument to get MIDI locale from * @bank: (out) (optional): Location to store instrument's * MIDI bank number or %NULL * @program: (out) (optional): Location to store instrument's * MIDI program number or %NULL * * Gets the MIDI locale of a virtual bank instrument (bank and program numbers). */ void ipatch_vbank_inst_get_midi_locale(IpatchVBankInst *inst, int *bank, int *program) { g_return_if_fail(IPATCH_IS_VBANK_INST(inst)); IPATCH_ITEM_RLOCK(inst); if(bank) { *bank = inst->bank; } if(program) { *program = inst->program; } IPATCH_ITEM_RUNLOCK(inst); } /** * ipatch_vbank_inst_compare: * @p1: First instrument in comparison * @p2: Second instrument in comparison * * Virtual bank instrument comparison function for sorting. Compare two * instruments by their MIDI bank:program numbers. Note that this function is * compatible with GCompareFunc and can therefore be used with g_list_sort, etc. * * Returns: Comparison result that is less than, equal to, or greater than zero * if @p1 is found, respectively, to be less than, to match, or be greater * than @p2. */ int ipatch_vbank_inst_compare(const IpatchVBankInst *p1, const IpatchVBankInst *p2) { gint32 aval, bval; aval = ((gint32)(p1->bank) << 16) | p1->program; bval = ((gint32)(p2->bank) << 16) | p2->program; return (aval - bval); } libinstpatch-1.1.6/libinstpatch/IpatchVBankInst.h000066400000000000000000000061241400263525300220330ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_VBANK_INST_H__ #define __IPATCH_VBANK_INST_H__ #include #include #include #include #include /* forward type declarations */ typedef struct _IpatchVBankInst IpatchVBankInst; typedef struct _IpatchVBankInstClass IpatchVBankInstClass; #define IPATCH_TYPE_VBANK_INST (ipatch_vbank_inst_get_type ()) #define IPATCH_VBANK_INST(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_VBANK_INST, \ IpatchVBankInst)) #define IPATCH_VBANK_INST_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_VBANK_INST, \ IpatchVBankInstClass)) #define IPATCH_IS_VBANK_INST(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VBANK_INST)) #define IPATCH_IS_VBANK_INST_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_VBANK_INST)) #define IPATCH_VBANK_INST_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_VBANK_INST, \ IpatchVBankInstClass)) /* Virtual bank instrument item */ struct _IpatchVBankInst { IpatchContainer parent_instance; char *name; /* name of instrument */ guint16 bank; /* MIDI bank map number */ guint16 program; /* MIDI program number */ GSList *regions; /* list of instrument regions */ }; struct _IpatchVBankInstClass { IpatchContainerClass parent_class; }; /** * IPATCH_VBANK_INST_NAME_SIZE: (skip) * * Maximum length of a virtual bank instrument name. */ #define IPATCH_VBANK_INST_NAME_SIZE 64 GType ipatch_vbank_inst_get_type(void); IpatchVBankInst *ipatch_vbank_inst_new(void); #define ipatch_vbank_inst_get_regions(inst) \ ipatch_container_get_children (IPATCH_CONTAINER (inst), \ IPATCH_TYPE_VBANK_REGION) IpatchVBankInst *ipatch_vbank_inst_first(IpatchIter *iter); IpatchVBankInst *ipatch_vbank_inst_next(IpatchIter *iter); void ipatch_vbank_inst_new_region(IpatchVBankInst *inst, IpatchItem *item); void ipatch_vbank_inst_set_midi_locale(IpatchVBankInst *inst, int bank, int program); void ipatch_vbank_inst_get_midi_locale(IpatchVBankInst *inst, int *bank, int *program); int ipatch_vbank_inst_compare(const IpatchVBankInst *p1, const IpatchVBankInst *p2); #endif libinstpatch-1.1.6/libinstpatch/IpatchVBankRegion.c000066400000000000000000000413061400263525300223350ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchVBankRegion * @short_description: Virtual bank instrument region * @see_also: #IpatchVBankInst, #IpatchVBank * @stability: Stable * * Virtual bank regions are children to #IpatchVBankInst objects and reference * synthesizable #IpatchItem objects from other files. This object forms the * bases for constructing new instruments from one or more items in other * instrument bank files. */ #include #include #include #include "IpatchVBankRegion.h" #include "ipatch_priv.h" #include "builtin_enums.h" enum { PROP_0, PROP_TITLE, PROP_LINK_ITEM, /* Region linked synthesis item */ PROP_ID_PROPS, /* Identification properties */ PROP_FILE_INDEX, /* Referenced file index in IpatchVBank parent */ PROP_NOTE_RANGE, /* Note range */ PROP_NOTE_RANGE_MODE, /* Mode of note range */ PROP_ROOT_NOTE, /* Root note */ PROP_ROOT_NOTE_MODE /* Mode of root note */ }; static void ipatch_vbank_region_get_title(IpatchVBankRegion *region, GValue *value); static void ipatch_vbank_region_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void ipatch_vbank_region_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void ipatch_vbank_region_finalize(GObject *object); static void ipatch_vbank_region_real_set_id_props(IpatchVBankRegion *region, char **id_props, gboolean notify); static void ipatch_vbank_region_remove_full(IpatchItem *item, gboolean full); static void ipatch_vbank_region_real_set_item(IpatchVBankRegion *region, IpatchItem *item, gboolean sample_notify); G_DEFINE_TYPE(IpatchVBankRegion, ipatch_vbank_region, IPATCH_TYPE_ITEM) static GParamSpec *link_item_pspec; static void ipatch_vbank_region_class_init(IpatchVBankRegionClass *klass) { GObjectClass *obj_class = G_OBJECT_CLASS(klass); IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass); obj_class->get_property = ipatch_vbank_region_get_property; obj_class->finalize = ipatch_vbank_region_finalize; item_class->item_set_property = ipatch_vbank_region_set_property; item_class->remove_full = ipatch_vbank_region_remove_full; g_object_class_override_property(obj_class, PROP_TITLE, "title"); link_item_pspec = g_param_spec_object("link-item", _("Link item"), _("Link item"), IPATCH_TYPE_ITEM, G_PARAM_READWRITE); g_object_class_install_property(obj_class, PROP_LINK_ITEM, link_item_pspec); g_object_class_install_property(obj_class, PROP_ID_PROPS, g_param_spec_value_array("id-props", _("ID props"), _("Identification properties"), g_param_spec_string("name-value", "Name value", "Name value pairs", NULL, G_PARAM_READWRITE), G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_FILE_INDEX, g_param_spec_uint("file-index", _("File index"), _("File index"), 0, G_MAXUINT, 0, G_PARAM_READABLE)); g_object_class_install_property(obj_class, PROP_NOTE_RANGE, ipatch_param_spec_range("note-range", _("Note range"), _("Note range"), 0, 127, 0, 127, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_NOTE_RANGE_MODE, g_param_spec_enum("note-range-mode", _("Note range mode"), _("Note range mode"), IPATCH_TYPE_VBANK_REGION_NOTE_RANGE_MODE, IPATCH_VBANK_REGION_NOTE_RANGE_MODE_INTERSECT, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_ROOT_NOTE, g_param_spec_int("root-note", _("Root note"), _("Root note"), -127, 127, 0, G_PARAM_READWRITE)); g_object_class_install_property(obj_class, PROP_ROOT_NOTE_MODE, g_param_spec_enum("root-note-mode", _("Root note mode"), _("Root note mode"), IPATCH_TYPE_VBANK_REGION_ROOT_NOTE_MODE, IPATCH_VBANK_REGION_ROOT_NOTE_MODE_OFFSET, G_PARAM_READWRITE)); } static void ipatch_vbank_region_get_title(IpatchVBankRegion *region, GValue *value) { IpatchItem *ref; char *s = NULL; g_object_get(region, "link-item", &ref, NULL); /* ++ ref region linked item */ if(ref) { g_object_get(ref, "title", &s, NULL); g_object_unref(ref); /* -- unref region linked item */ g_value_take_string(value, s); } else { g_value_set_static_string(value, _("")); } } static void ipatch_vbank_region_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { IpatchVBankRegion *region = IPATCH_VBANK_REGION(object); GValueArray *valarray; IpatchRange *range; char **strv; guint i; switch(property_id) { case PROP_LINK_ITEM: ipatch_vbank_region_real_set_item(region, (IpatchItem *)g_value_get_object(value), FALSE); break; case PROP_ID_PROPS: valarray = g_value_get_boxed(value); if(valarray) { strv = g_new(char *, valarray->n_values + 1); /* ++ alloc */ for(i = 0; i < valarray->n_values; i++) { strv[i] = (char *)g_value_get_string(g_value_array_get_nth(valarray, i)); } strv[valarray->n_values] = NULL; ipatch_vbank_region_real_set_id_props(region, strv, FALSE); g_free(strv); /* -- free - strings themselves were not allocated */ } break; case PROP_NOTE_RANGE: range = ipatch_value_get_range(value); IPATCH_ITEM_WLOCK(region); region->note_range = *range; IPATCH_ITEM_WUNLOCK(region); break; case PROP_NOTE_RANGE_MODE: region->note_range_mode = g_value_get_enum(value); break; case PROP_ROOT_NOTE: region->root_note = g_value_get_int(value); break; case PROP_ROOT_NOTE_MODE: region->root_note_mode = g_value_get_enum(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_vbank_region_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { IpatchVBankRegion *region = IPATCH_VBANK_REGION(object); GValueArray *valarray; IpatchRange range; guint n_elements; char **strv; GValue val = { 0 }; guint i; switch(property_id) { case PROP_TITLE: ipatch_vbank_region_get_title(region, value); break; case PROP_LINK_ITEM: g_value_take_object(value, ipatch_vbank_region_get_item(region)); break; case PROP_ID_PROPS: strv = ipatch_vbank_region_get_id_props(region, &n_elements); /* ++ alloc */ if(strv) { valarray = g_value_array_new(n_elements); /* ++ alloc */ g_value_init(&val, G_TYPE_STRING); for(i = 0; i < n_elements; i++) { g_value_set_string(&val, strv[i]); /* ++ alloc */ g_value_array_append(valarray, &val); g_value_reset(&val); /* -- free */ } g_value_unset(&val); g_value_take_boxed(value, valarray); /* !! owned */ g_strfreev(strv); /* -- free */ } else { g_value_set_boxed(value, NULL); } break; case PROP_FILE_INDEX: g_value_set_uint(value, region->file_index); break; case PROP_NOTE_RANGE: IPATCH_ITEM_WLOCK(region); range = region->note_range; IPATCH_ITEM_WUNLOCK(region); ipatch_value_set_range(value, &range); break; case PROP_NOTE_RANGE_MODE: g_value_set_enum(value, region->note_range_mode); break; case PROP_ROOT_NOTE: g_value_set_int(value, region->root_note); break; case PROP_ROOT_NOTE_MODE: g_value_set_enum(value, region->root_note_mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } } static void ipatch_vbank_region_remove_full(IpatchItem *item, gboolean full) { if(full) { ipatch_vbank_region_real_set_item(IPATCH_VBANK_REGION(item), NULL, TRUE); } if(IPATCH_ITEM_CLASS(ipatch_vbank_region_parent_class)->remove_full) { IPATCH_ITEM_CLASS(ipatch_vbank_region_parent_class)->remove_full(item, full); } } static void ipatch_vbank_region_init(IpatchVBankRegion *region) { region->note_range.low = 0; region->note_range.high = 127; } static void ipatch_vbank_region_finalize(GObject *object) { IpatchVBankRegion *region = IPATCH_VBANK_REGION(object); if(region->item) { g_object_unref(region->item); } g_strfreev(region->id_props); if(G_OBJECT_CLASS(ipatch_vbank_region_parent_class)->finalize) { G_OBJECT_CLASS(ipatch_vbank_region_parent_class)->finalize(object); } } /** * ipatch_vbank_region_new: * * Create a new virtual bank region object. * * Returns: New virtual bank region with a reference count of 1. Caller * owns the reference and removing it will destroy the item, unless another * reference is added (if its parented for example). */ IpatchVBankRegion * ipatch_vbank_region_new(void) { return (IPATCH_VBANK_REGION(g_object_new(IPATCH_TYPE_VBANK_REGION, NULL))); } /** * ipatch_vbank_region_first: (skip) * @iter: Patch item iterator containing #IpatchVBankRegion items * * Gets the first item in a virtual bank region iterator. A convenience * wrapper for ipatch_iter_first(). * * Returns: The first virtual bank region in @iter or %NULL if empty. */ IpatchVBankRegion * ipatch_vbank_region_first(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_first(iter); if(obj) { return (IPATCH_VBANK_REGION(obj)); } else { return (NULL); } } /** * ipatch_vbank_region_next: (skip) * @iter: Patch item iterator containing #IpatchVBankRegion items * * Gets the next item in a virtual bank region iterator. A convenience wrapper * for ipatch_iter_next(). * * Returns: The next virtual bank region in @iter or %NULL if at the end of * the list. */ IpatchVBankRegion * ipatch_vbank_region_next(IpatchIter *iter) { GObject *obj; g_return_val_if_fail(iter != NULL, NULL); obj = ipatch_iter_next(iter); if(obj) { return (IPATCH_VBANK_REGION(obj)); } else { return (NULL); } } /** * ipatch_vbank_region_set_id_props: * @region: VBank region * @id_props: (array zero-terminated=1): NULL terminated array of name/value string pairs * * Set the ID properties of a virtual bank region. These are used to uniquely * identify an item in an external instrument file. This function is likely * only used by the VBank loader before an item is resolved. Normal users * will likely just assign to the "item-link" parameter. */ void ipatch_vbank_region_set_id_props(IpatchVBankRegion *region, char **id_props) { ipatch_vbank_region_real_set_id_props(region, id_props, TRUE); } /* the real set ID properties routine, takes a notify parameter to indicate if * a property notify should be done */ static void ipatch_vbank_region_real_set_id_props(IpatchVBankRegion *region, char **id_props, gboolean notify) { char **dup_id_props; g_return_if_fail(IPATCH_IS_VBANK_REGION(region)); dup_id_props = g_strdupv(id_props); /* ++ alloc */ IPATCH_ITEM_WLOCK(region); g_strfreev(region->id_props); region->id_props = dup_id_props; /* !! owned */ IPATCH_ITEM_WUNLOCK(region); if(notify) { g_object_notify(G_OBJECT(region), "id-props"); } } /** * ipatch_vbank_region_get_id_props: * @region: VBank region * @n_elements: (out) (optional): Pointer to store count of elements in * returned string array or %NULL to ignore. * * Get ID properties which uniquely identify the referenced item. These are * usually only available until the item gets resolved, at which point * "item-link" is set. * * Returns: (array zero-terminated=1) (transfer full): %NULL terminated array of name/value * pair property strings or %NULL if none. Free with g_strfreev() when finished using it. */ char ** ipatch_vbank_region_get_id_props(IpatchVBankRegion *region, guint *n_elements) { char **id_props; g_return_val_if_fail(IPATCH_IS_VBANK_REGION(region), NULL); IPATCH_ITEM_RLOCK(region); id_props = g_strdupv(region->id_props); /* ++ alloc */ IPATCH_ITEM_RUNLOCK(region); if(n_elements) { *n_elements = id_props ? g_strv_length(id_props) : 0; } return (id_props); /* !! owned */ } /** * ipatch_vbank_region_set_item: * @region: Region to set sample of * @item: Instrument item to assign * * Sets the referenced instrument item of a virtual bank region. * * Since: 1.1.0 */ void ipatch_vbank_region_set_item(IpatchVBankRegion *region, IpatchItem *item) { g_return_if_fail(IPATCH_IS_VBANK_REGION(region)); g_return_if_fail(IPATCH_IS_ITEM(item)); ipatch_vbank_region_real_set_item(region, item, TRUE); } static void ipatch_vbank_region_real_set_item(IpatchVBankRegion *region, IpatchItem *item, gboolean sample_notify) { GValue newval = { 0 }, oldval = { 0 }; IpatchItem *olditem; if(item) { g_object_ref(item); // !! ref item for region } IPATCH_ITEM_WLOCK(region); olditem = region->item; region->item = item; IPATCH_ITEM_WUNLOCK(region); if(sample_notify) { g_value_init(&oldval, IPATCH_TYPE_ITEM); g_value_set_object(&oldval, olditem); g_value_init(&newval, IPATCH_TYPE_ITEM); g_value_set_object(&newval, item); ipatch_item_prop_notify((IpatchItem *)region, link_item_pspec, &newval, &oldval); g_value_unset(&newval); g_value_unset(&oldval); } if(olditem) { g_object_unref(olditem); // !! unref old item (no longer referenced by region) } /* notify title property change */ g_value_init(&newval, G_TYPE_STRING); ipatch_vbank_region_get_title(region, &newval); ipatch_item_prop_notify((IpatchItem *)region, ipatch_item_pspec_title, &newval, NULL); g_value_unset(&newval); } /** * ipatch_vbank_region_get_item: * @region: Region to get referenced instrument item from * * Gets the referenced instrument item from a region. * * Returns: (transfer full): Region's referenced item or %NULL if not set yet. Remember to * unreference the item with g_object_unref() when done with it. * * Since: 1.1.0 */ IpatchItem * ipatch_vbank_region_get_item(IpatchVBankRegion *region) { IpatchItem *item; g_return_val_if_fail(IPATCH_IS_VBANK_REGION(region), NULL); IPATCH_ITEM_RLOCK(region); item = region->item; if(item) { g_object_ref(item); // ++ reference for caller } IPATCH_ITEM_RUNLOCK(region); return (item); // !! caller takes on reference } libinstpatch-1.1.6/libinstpatch/IpatchVBankRegion.h000066400000000000000000000102001400263525300223270ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_VBANK_REGION_H__ #define __IPATCH_VBANK_REGION_H__ #include #include #include #include /* forward type declarations */ typedef struct _IpatchVBankRegion IpatchVBankRegion; typedef struct _IpatchVBankRegionClass IpatchVBankRegionClass; #define IPATCH_TYPE_VBANK_REGION (ipatch_vbank_region_get_type ()) #define IPATCH_VBANK_REGION(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_VBANK_REGION, \ IpatchVBankRegion)) #define IPATCH_VBANK_REGION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_VBANK_REGION, \ IpatchVBankRegionClass)) #define IPATCH_IS_VBANK_REGION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VBANK_REGION)) #define IPATCH_IS_VBANK_REGION_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), IPATCH_TYPE_VBANK_REGION)) #define IPATCH_VBANK_REGION_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), IPATCH_TYPE_VBANK_REGION, \ IpatchVBankRegionClass)) /* Virtual bank region */ struct _IpatchVBankRegion { IpatchItem parent_instance; /*< private >*/ IpatchItem *item; /* Referenced item or NULL (if unresolved) */ char **id_props; /* prop/val pairs which ID item or NULL (if resolved) */ guint file_index; /* Index of file in IpatchVBank parent (if unresolved) */ IpatchRange note_range; /* MIDI note range of this region */ guint8 note_range_mode; /* #IpatchVBankRegionNoteRangeMode */ gint8 root_note; /* MIDI root note value */ guint8 root_note_mode; /* #IpatchVBankRegionRootNoteMode */ }; struct _IpatchVBankRegionClass { IpatchItemClass parent_class; }; /** * IpatchVBankNoteRangeMode: * @IPATCH_VBANK_REGION_NOTE_RANGE_MODE_INTERSECT: Note range is applied as an * intersection with existing voice note ranges (only those notes shared by * both ranges will become the final range, a logic AND operation). * @IPATCH_VBANK_REGION_NOTE_RANGE_MODE_ASSIGN: Note range of all voices is * overridden by new range. * * Determines mode in which a virtual bank region's note range is applied to the * affected synthesis voices. */ typedef enum { IPATCH_VBANK_REGION_NOTE_RANGE_MODE_INTERSECT = 0, IPATCH_VBANK_REGION_NOTE_RANGE_MODE_OVERRIDE = 1 } IpatchVBankRegionNoteRangeMode; /** * IpatchVBankRootNoteMode: * @IPATCH_VBANK_REGION_ROOT_NOTE_MODE_OFFSET: Offset the root note parameters of * affected synthesis voices by a given signed integer amount. * @IPATCH_VBANK_REGION_ROOT_NOTE_MODE_OVERRIDE: Override root note parameters of * affected synthesis voices. */ typedef enum { IPATCH_VBANK_REGION_ROOT_NOTE_MODE_OFFSET = 0, IPATCH_VBANK_REGION_ROOT_NOTE_MODE_OVERRIDE = 1 } IpatchVBankRegionRootNoteMode; GType ipatch_vbank_region_get_type(void); IpatchVBankRegion *ipatch_vbank_region_new(void); IpatchVBankRegion *ipatch_vbank_region_first(IpatchIter *iter); IpatchVBankRegion *ipatch_vbank_region_next(IpatchIter *iter); void ipatch_vbank_region_set_id_props(IpatchVBankRegion *region, char **id_props); char **ipatch_vbank_region_get_id_props(IpatchVBankRegion *region, guint *n_elements); void ipatch_vbank_region_set_item(IpatchVBankRegion *region, IpatchItem *item); IpatchItem *ipatch_vbank_region_get_item(IpatchVBankRegion *region); #endif libinstpatch-1.1.6/libinstpatch/IpatchVirtualContainer.c000066400000000000000000000035631400263525300234640ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchVirtualContainer * @short_description: Virtual container object * @see_also: * @stability: Stable * * Virtual containers are used in user interfaces to group items in * containers that aren't actually present in the hierarchy, such as * "Instruments", "Melodic Presets", "Percussion Presets" in SF2 files. */ #include #include #include #include "IpatchVirtualContainer.h" #include "IpatchTypeProp.h" GType ipatch_virtual_container_get_type(void) { static GType item_type = 0; if(!item_type) { static const GTypeInfo item_info = { sizeof(IpatchVirtualContainerClass), NULL, NULL, (GClassInitFunc) NULL, NULL, NULL, sizeof(IpatchVirtualContainer), 0, (GInstanceInitFunc) NULL, }; item_type = g_type_register_static(IPATCH_TYPE_ITEM, "IpatchVirtualContainer", &item_info, G_TYPE_FLAG_ABSTRACT); } return (item_type); } libinstpatch-1.1.6/libinstpatch/IpatchVirtualContainer.h000066400000000000000000000062771400263525300234760ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_VIRTUAL_CONTAINER_H__ #define __IPATCH_VIRTUAL_CONTAINER_H__ #include #include /* forward type declarations */ typedef struct _IpatchVirtualContainer IpatchVirtualContainer; typedef struct _IpatchVirtualContainerClass IpatchVirtualContainerClass; #include #define IPATCH_TYPE_VIRTUAL_CONTAINER (ipatch_virtual_container_get_type ()) #define IPATCH_VIRTUAL_CONTAINER(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), IPATCH_TYPE_VIRTUAL_CONTAINER, \ IpatchVirtualContainer)) #define IPATCH_VIRTUAL_CONTAINER_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), IPATCH_TYPE_VIRTUAL_CONTAINER, \ IpatchVirtualContainerClass)) #define IPATCH_IS_VIRTUAL_CONTAINER(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VIRTUAL_CONTAINER)) /** * IPATCH_VIRTUAL_CONTAINER_CREATE: * @type_under: Type string in the form "type_name" * @TypeCase: Type string in the form "TypeName" * @name: Name type property * @blurb: Blurb type property (more detailed description) * @childtype: Child type of this virtual container type */ #define IPATCH_VIRTUAL_CONTAINER_CREATE(type_under, TypeCase, name, blurb, childtype) \ GType type_under##_get_type (void) \ { \ static GType obj_type = 0; \ \ if (!obj_type) { \ static const GTypeInfo obj_info = { \ sizeof (IpatchVirtualContainerClass), NULL, NULL, \ (GClassInitFunc) NULL, NULL, NULL, \ sizeof (IpatchVirtualContainer), 0, \ (GInstanceInitFunc) NULL, \ }; \ \ obj_type = g_type_register_static (IPATCH_TYPE_VIRTUAL_CONTAINER, \ #TypeCase, &obj_info, 0); \ ipatch_type_set (obj_type, \ "name", name, \ "blurb", blurb, \ "virtual-child-type", childtype, NULL); \ } \ \ return (obj_type); \ } /* virtual container object */ struct _IpatchVirtualContainer { IpatchItem parent_instance; /* derived from IpatchItem */ }; /* virtual container class */ struct _IpatchVirtualContainerClass { IpatchItemClass parent_class; }; /** * IpatchVirtualContainerConformFunc: * @object: Object to conform to a virtual container criteria. * * A function type used to make an item conform to the criteria of a virtual * container (force a SoundFont preset to be a percussion preset for example). */ typedef void (*IpatchVirtualContainerConformFunc)(GObject *object); GType ipatch_virtual_container_get_type(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchVirtualContainer_types.c000066400000000000000000000064561400263525300247140ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchVirtualContainer_types * @short_description: Built in virtual container types * @see_also: * @stability: Stable */ #include #include #include "IpatchVirtualContainer_types.h" #include "IpatchDLS2Inst.h" #include "IpatchDLS2Sample.h" #include "IpatchGigInst.h" #include "IpatchGigSample.h" #include "IpatchSF2Preset.h" #include "IpatchSF2Inst.h" #include "IpatchSF2Sample.h" #include "IpatchSLIInst.h" #include "IpatchSLISample.h" #include "IpatchTypeProp.h" #include "i18n.h" IPATCH_VIRTUAL_CONTAINER_CREATE (ipatch_virtual_dls2_melodic, IpatchVirtualDLS2Melodic, _("Melodic Instruments"), _("Non percussion instruments"), IPATCH_TYPE_DLS2_INST) IPATCH_VIRTUAL_CONTAINER_CREATE (ipatch_virtual_dls2_percussion, IpatchVirtualDLS2Percussion, _("Percussion Instruments"), _("Percussion Instruments"), IPATCH_TYPE_DLS2_INST) IPATCH_VIRTUAL_CONTAINER_CREATE (ipatch_virtual_dls2_samples, IpatchVirtualDLS2Samples, _("Samples"), _("Samples"), IPATCH_TYPE_DLS2_SAMPLE) IPATCH_VIRTUAL_CONTAINER_CREATE (ipatch_virtual_gig_melodic, IpatchVirtualGigMelodic, _("Melodic Instruments"), _("Non percussion instruments"), IPATCH_TYPE_GIG_INST) IPATCH_VIRTUAL_CONTAINER_CREATE (ipatch_virtual_gig_percussion, IpatchVirtualGigPercussion, _("Percussion Instruments"), _("Percussion Instruments"), IPATCH_TYPE_GIG_INST) IPATCH_VIRTUAL_CONTAINER_CREATE (ipatch_virtual_gig_samples, IpatchVirtualGigSamples, _("Samples"), _("Samples"), IPATCH_TYPE_GIG_SAMPLE) IPATCH_VIRTUAL_CONTAINER_CREATE (ipatch_virtual_sf2_inst, IpatchVirtualSF2Inst, _("Instruments"), _("Instruments"), IPATCH_TYPE_SF2_INST) IPATCH_VIRTUAL_CONTAINER_CREATE (ipatch_virtual_sf2_melodic, IpatchVirtualSF2Melodic, _("Melodic Presets"), _("Non percussion presets"), IPATCH_TYPE_SF2_PRESET) IPATCH_VIRTUAL_CONTAINER_CREATE (ipatch_virtual_sf2_percussion, IpatchVirtualSF2Percussion, _("Percussion Presets"), _("Percussion Presets"), IPATCH_TYPE_SF2_PRESET) IPATCH_VIRTUAL_CONTAINER_CREATE (ipatch_virtual_sf2_samples, IpatchVirtualSF2Samples, _("Samples"), _("Samples"), IPATCH_TYPE_SF2_SAMPLE) IPATCH_VIRTUAL_CONTAINER_CREATE (ipatch_virtual_sf2_rom, IpatchVirtualSF2Rom, _("ROM Samples"), _("ROM Samples"), IPATCH_TYPE_SF2_SAMPLE) IPATCH_VIRTUAL_CONTAINER_CREATE (ipatch_virtual_sli_inst, IpatchVirtualSLIInst, _("Instruments"), _("Instruments"), IPATCH_TYPE_SLI_INST) IPATCH_VIRTUAL_CONTAINER_CREATE (ipatch_virtual_sli_samples, IpatchVirtualSLISamples, _("Samples"), _("Samples"), IPATCH_TYPE_SLI_SAMPLE) libinstpatch-1.1.6/libinstpatch/IpatchVirtualContainer_types.h000066400000000000000000000103671400263525300247150ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_VIRTUAL_CONTAINER_TYPES_H__ #define __IPATCH_VIRTUAL_CONTAINER_TYPES_H__ #include #include /* forward type declarations */ #include #define IPATCH_TYPE_VIRTUAL_DLS2_MELODIC \ (ipatch_virtual_dls2_melodic_get_type ()) #define IPATCH_IS_VIRTUAL_DLS2_MELODIC(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VIRTUAL_DLS2_MELODIC)) #define IPATCH_TYPE_VIRTUAL_DLS2_PERCUSSION \ (ipatch_virtual_dls2_percussion_get_type ()) #define IPATCH_IS_VIRTUAL_DLS2_PERCUSSION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VIRTUAL_DLS2_PERCUSSION)) #define IPATCH_TYPE_VIRTUAL_DLS2_SAMPLES \ (ipatch_virtual_dls2_samples_get_type ()) #define IPATCH_IS_VIRTUAL_DLS2_SAMPLES(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VIRTUAL_DLS2_SAMPLES)) #define IPATCH_TYPE_VIRTUAL_GIG_MELODIC \ (ipatch_virtual_gig_melodic_get_type ()) #define IPATCH_IS_VIRTUAL_GIG_MELODIC(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VIRTUAL_GIG_MELODIC)) #define IPATCH_TYPE_VIRTUAL_GIG_PERCUSSION \ (ipatch_virtual_gig_percussion_get_type ()) #define IPATCH_IS_VIRTUAL_GIG_PERCUSSION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VIRTUAL_GIG_PERCUSSION)) #define IPATCH_TYPE_VIRTUAL_GIG_SAMPLES \ (ipatch_virtual_gig_samples_get_type ()) #define IPATCH_IS_VIRTUAL_GIG_SAMPLES(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VIRTUAL_GIG_SAMPLES)) #define IPATCH_TYPE_VIRTUAL_SF2_INST \ (ipatch_virtual_sf2_inst_get_type ()) #define IPATCH_IS_VIRTUAL_SF2_INST(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VIRTUAL_SF2_INST)) #define IPATCH_TYPE_VIRTUAL_SF2_MELODIC \ (ipatch_virtual_sf2_melodic_get_type ()) #define IPATCH_IS_VIRTUAL_SF2_MELODIC(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VIRTUAL_SF2_MELODIC)) #define IPATCH_TYPE_VIRTUAL_SF2_PERCUSSION \ (ipatch_virtual_sf2_percussion_get_type ()) #define IPATCH_IS_VIRTUAL_SF2_PERCUSSION(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VIRTUAL_SF2_PERCUSSION)) #define IPATCH_TYPE_VIRTUAL_SF2_SAMPLES \ (ipatch_virtual_sf2_samples_get_type ()) #define IPATCH_IS_VIRTUAL_SF2_SAMPLES(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VIRTUAL_SF2_SAMPLES)) #define IPATCH_TYPE_VIRTUAL_SF2_ROM \ (ipatch_virtual_sf2_rom_get_type ()) #define IPATCH_IS_VIRTUAL_SF2_ROM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VIRTUAL_SF2_ROM)) #define IPATCH_TYPE_VIRTUAL_SLI_INST \ (ipatch_virtual_sli_inst_get_type ()) #define IPATCH_IS_VIRTUAL_SLI_INST(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VIRTUAL_SLI_INST)) #define IPATCH_TYPE_VIRTUAL_SLI_SAMPLES \ (ipatch_virtual_sli_samples_get_type ()) #define IPATCH_IS_VIRTUAL_SLI_SAMPLES(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IPATCH_TYPE_VIRTUAL_SLI_SAMPLES)) GType ipatch_virtual_dls2_melodic_get_type(void); GType ipatch_virtual_dls2_percussion_get_type(void); GType ipatch_virtual_dls2_samples_get_type(void); GType ipatch_virtual_gig_melodic_get_type(void); GType ipatch_virtual_gig_percussion_get_type(void); GType ipatch_virtual_gig_samples_get_type(void); GType ipatch_virtual_sf2_inst_get_type(void); GType ipatch_virtual_sf2_melodic_get_type(void); GType ipatch_virtual_sf2_percussion_get_type(void); GType ipatch_virtual_sf2_samples_get_type(void); GType ipatch_virtual_sf2_rom_get_type(void); GType ipatch_virtual_sli_inst_get_type(void); GType ipatch_virtual_sli_samples_get_type(void); #endif libinstpatch-1.1.6/libinstpatch/IpatchXml.c000066400000000000000000000700211400263525300207240ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchXml * @short_description: XML tree functions * @see_also: IpatchXmlObject * @stability: Stable * * Functions for manipulating XML node trees and saving/loading to/from * XML content in strings or files. XML node trees use the glib GNode N-ary * tree data type for added flexibility. */ #include #include "IpatchXml.h" #include "misc.h" static gboolean xml_destroy_traverse_func(GNode *node, gpointer data); static GNode *ipatch_xml_find_by_path_recurse(GNode *node, const char *path); static void ipatch_xml_to_str_recurse(GString *str, GNode *node, guint indent, guint inc); static void xml_start_element(GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error); static void xml_end_element(GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error); static void xml_text(GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error); /** * ipatch_xml_new_node: (skip) * @parent: (nullable): Parent node to add new * node to as a child, or %NULL to create new root node * @name: Name of the new XML node * @value: (nullable): Text value to assign to the new node or %NULL * @attr_name: (nullable): First attribute name to assign or %NULL * @...: (type char*): If @attr_name was supplied first string value to be assigned should be * the first parameter, additional name/value attribute string pairs may * follow terminated by a %NULL name. * * Create a new XML tree node and append it to the given @parent, if supplied. * Note that the returned GNode can be used with other N-Array glib operations. * * Returns: New XML tree node */ GNode * ipatch_xml_new_node(GNode *parent, const char *name, const char *value, const char *attr_name, ...) { IpatchXmlNode *xmlnode; IpatchXmlAttr *attr; va_list var_args; char *vname, *vvalue; g_return_val_if_fail(name != NULL, NULL); xmlnode = ipatch_xml_node_new(); xmlnode->name = g_strdup(name); xmlnode->value = g_strdup(value); xmlnode->attributes = NULL; if(attr_name) { va_start(var_args, attr_name); attr = ipatch_xml_attr_new(); attr->name = g_strdup(attr_name); attr->value = g_strdup(va_arg(var_args, char *)); xmlnode->attributes = g_list_append(xmlnode->attributes, attr); while((vname = va_arg(var_args, char *))) { vvalue = va_arg(var_args, char *); if(!vvalue) { continue; } attr = ipatch_xml_attr_new(); attr->name = g_strdup(vname); attr->value = g_strdup(vvalue); xmlnode->attributes = g_list_append(xmlnode->attributes, attr); } va_end(var_args); } return (parent ? g_node_append_data(parent, xmlnode) : g_node_new(xmlnode)); } /** * ipatch_xml_new_node_strv: (skip) * @parent: (nullable): Parent node to add * new node to as a child, or %NULL to create new root node * @name: Name of the new XML node * @value: (nullable): Text value to assign to the new node or %NULL * @attr_names: (array zero-terminated=1) (nullable): %NULL terminated * array of attribute names or %NULL * @attr_values: (array zero-terminated=1) (nullable): %NULL terminated * array of attribute values or %NULL * * Like ipatch_xml_new_node() but takes attribute name/values as separate strv * arrays. * * Returns: New XML tree node */ GNode * ipatch_xml_new_node_strv(GNode *parent, const char *name, const char *value, const char **attr_names, const char **attr_values) { IpatchXmlNode *xmlnode; IpatchXmlAttr *attr; int i; g_return_val_if_fail(name != NULL, NULL); g_return_val_if_fail(!attr_names == !attr_values, NULL); xmlnode = ipatch_xml_node_new(); xmlnode->name = g_strdup(name); xmlnode->value = g_strdup(value); xmlnode->attributes = NULL; if(attr_names) { for(i = 0; attr_names[i] && attr_values[i]; i++) { if(!attr_values[i]) { continue; } attr = ipatch_xml_attr_new(); attr->name = g_strdup(attr_names[i]); attr->value = g_strdup(attr_values[i]); xmlnode->attributes = g_list_append(xmlnode->attributes, attr); } } return (parent ? g_node_append_data(parent, xmlnode) : g_node_new(xmlnode)); } #define QDATA(node) &(((IpatchXmlNode *)(node->data))->qdata) /** * ipatch_xml_get_data: (skip) * @node: XML node * @key: Name of the key * * Lookup data assigned to an XML node. * * Returns: The data pointer or %NULL if not set */ gpointer ipatch_xml_get_data(GNode *node, const char *key) { g_return_val_if_fail(node != NULL, NULL); return (g_datalist_get_data(QDATA(node), key)); } /** * ipatch_xml_set_data: (skip) * @node: XML node * @key: Name of the key * @data: Data to associate with the key * * Assigns arbitrary data to an XML node specified by a @key. */ void ipatch_xml_set_data(GNode *node, const char *key, gpointer data) { g_return_if_fail(node != NULL); g_datalist_set_data(QDATA(node), key, data); } /** * ipatch_xml_set_data_full: (skip) * @node: XML node * @key: Name of the key * @data: Data to associate with the key * @destroy_func: (nullable): Destroy function or %NULL * * Assigns arbitrary data to an XML node specified by a @key. Also assigns a * @destroy_func callback to destroy the data when it is removed. */ void ipatch_xml_set_data_full(GNode *node, const char *key, gpointer data, GDestroyNotify destroy_func) { g_return_if_fail(node != NULL); g_datalist_set_data_full(QDATA(node), key, data, destroy_func); } /** * ipatch_xml_steal_data: (skip) * @node: XML node * @key: Name of the key * * Remove keyed data from an XML node, but don't call the data's destroy notify. * Caller is thus given the ownership of the data. * * Returns: (transfer none): The data pointer or %NULL if not set */ gpointer ipatch_xml_steal_data(GNode *node, const char *key) { gpointer data; GQuark quark; g_return_val_if_fail(node != NULL, NULL); quark = g_quark_try_string(key); if(!quark) { return (NULL); } data = g_datalist_id_get_data(QDATA(node), quark); if(data) { g_datalist_id_remove_no_notify(QDATA(node), quark); } return (data); } /** * ipatch_xml_get_qdata: (skip) * @node: XML node * @quark: Quark key * * Lookup data assigned to an XML node using a quark. This is faster than * ipatch_xml_get_data() since the key must be converted to a quark anyways. * * Returns: (transfer none): The data pointer or %NULL if not set */ gpointer ipatch_xml_get_qdata(GNode *node, GQuark quark) { g_return_val_if_fail(node != NULL, NULL); return (g_datalist_id_get_data(QDATA(node), quark)); } /** * ipatch_xml_set_qdata: (skip) * @node: XML node * @quark: Quark key * @data: Data to associate with the key * * Assigns arbitrary data to an XML node specified by a @quark key. This is * faster than ipatch_xml_set_data() since the key must be converted to a quark * anyways. */ void ipatch_xml_set_qdata(GNode *node, GQuark quark, gpointer data) { g_return_if_fail(node != NULL); g_datalist_id_set_data(QDATA(node), quark, data); } /** * ipatch_xml_set_qdata_full: (skip) * @node: XML node * @quark: Quark key * @data: Data to associate with the key * @destroy_func: (nullable): Destroy function or %NULL * * Assigns arbitrary data to an XML node specified by a @key. Also assigns a * @destroy_func callback to destroy the data when it is removed. This is * faster than ipatch_xml_set_data_full() since the key must be converted to a quark * anyways. */ void ipatch_xml_set_qdata_full(GNode *node, GQuark quark, gpointer data, GDestroyNotify destroy_func) { g_return_if_fail(node != NULL); g_datalist_id_set_data_full(QDATA(node), quark, data, destroy_func); } /** * ipatch_xml_steal_qdata: (skip) * @node: XML node * @quark: Quark key * * Remove keyed data from an XML node, but don't call the data's destroy notify. * Caller is thus given the ownership of the data. This is faster than * ipatch_xml_steal_data() since the key must be converted to a quark * anyways. * * Returns: (transfer none): The data pointer or %NULL if not set */ gpointer ipatch_xml_steal_qdata(GNode *node, GQuark quark) { gpointer data; g_return_val_if_fail(node != NULL, NULL); data = g_datalist_id_get_data(QDATA(node), quark); if(data) { g_datalist_id_remove_no_notify(QDATA(node), quark); } return (data); } /** * ipatch_xml_destroy: (skip) * @node: Root of XML tree/sub tree to destroy * * Free an XML tree (a root @node and all its children). Does not need to be * the actual root of a tree, i.e., can remove a sub tree. */ void ipatch_xml_destroy(GNode *node) { g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, xml_destroy_traverse_func, NULL); g_node_destroy(node); } static gboolean xml_destroy_traverse_func(GNode *node, gpointer data) { ipatch_xml_node_free(node->data); return (FALSE); } /** * ipatch_xml_copy: (skip) * @node: XML tree to copy * * Perform a deep copy on an XML tree. * * Returns: New duplicate XML tree. */ GNode * ipatch_xml_copy(GNode *node) { g_return_val_if_fail(node != NULL, NULL); return (g_node_copy_deep(node, (GCopyFunc)ipatch_xml_node_duplicate, NULL)); } /** * ipatch_xml_set_name: (skip) * @node: XML node * @name: Name to assign * * Set the name of an XML node. */ void ipatch_xml_set_name(GNode *node, const char *name) { IpatchXmlNode *xmlnode; g_return_if_fail(node != NULL); g_return_if_fail(name != NULL); xmlnode = node->data; g_free(xmlnode->name); xmlnode->name = g_strdup(name); } /** * ipatch_xml_set_value: (skip) * @node: XML node * @value: Text value to assign or %NULL to clear it * * Set the text value of an XML node. */ void ipatch_xml_set_value(GNode *node, const char *value) { IpatchXmlNode *xmlnode; g_return_if_fail(node != NULL); xmlnode = node->data; g_free(xmlnode->value); xmlnode->value = g_strdup(value); } /** * ipatch_xml_set_value_printf: (skip) * @node: XML node * @format: Printf format * @...: Printf arguments * * Assign a value to an XML node using a printf format and arguments. */ void ipatch_xml_set_value_printf(GNode *node, const char *format, ...) { va_list var_args; char *value; g_return_if_fail(node != NULL); g_return_if_fail(format != NULL); va_start(var_args, format); value = g_strdup_vprintf(format, var_args); va_end(var_args); ipatch_xml_take_value(node, value); } /** * ipatch_xml_take_name: (skip) * @node: XML node * @name: (nullable) (transfer full): Name to assign or %NULL to clear it * * Like ipatch_xml_set_name() but takes over the allocation of @name rather than * duplicating it. */ void ipatch_xml_take_name(GNode *node, char *name) { IpatchXmlNode *xmlnode; g_return_if_fail(node != NULL); g_return_if_fail(name != NULL); xmlnode = node->data; g_free(xmlnode->name); xmlnode->name = name; } /** * ipatch_xml_take_value: (skip) * @node: XML node * @value: (transfer full): Text value to assign * * Like ipatch_xml_set_value() but takes over the allocation of @value rather than * duplicating it. */ void ipatch_xml_take_value(GNode *node, char *value) { IpatchXmlNode *xmlnode; g_return_if_fail(node != NULL); xmlnode = node->data; g_free(xmlnode->value); xmlnode->value = value; } /** * ipatch_xml_get_name: (skip) * @node: XML node to get name of * * Get the name of an XML node. * * Returns: Name of XML node which is internal and should not be modified or freed. */ G_CONST_RETURN char * ipatch_xml_get_name(GNode *node) { g_return_val_if_fail(node != NULL, NULL); return (((IpatchXmlNode *)(node->data))->name); } /** * ipatch_xml_test_name: (skip) * @node: XML node to get name of * @cmpname: Name to compare to * * Test if the node has the given name. * * Returns: %TRUE if the node has the given name, %FALSE otherwise */ gboolean ipatch_xml_test_name(GNode *node, const char *cmpname) { const char *name; g_return_val_if_fail(node != NULL, FALSE); g_return_val_if_fail(cmpname != NULL, FALSE); name = ipatch_xml_get_name(node); return (name && strcmp(name, cmpname) == 0); } /** * ipatch_xml_get_value: (skip) * @node: XML node to get value of * * Get the text value of an XML node. * * Returns: Value of XML node or %NULL, which is internal and should not be * modified or freed. */ G_CONST_RETURN char * ipatch_xml_get_value(GNode *node) { g_return_val_if_fail(node != NULL, NULL); return (((IpatchXmlNode *)(node->data))->value); } /** * ipatch_xml_dup_value: (skip) * @node: XML node to duplicate value of * * Duplicate the text value of an XML node. Like ipatch_xml_get_value() but * makes a copy of the value which the caller owns. * * Returns: Newly allocated duplicate value of XML node or %NULL. */ char * ipatch_xml_dup_value(GNode *node) { g_return_val_if_fail(node != NULL, NULL); return (g_strdup(((IpatchXmlNode *)(node->data))->value)); } /** * ipatch_xml_test_value: (skip) * @node: XML node to get name of * @cmpvalue: Value to compare to * * Test if the node has the given value. * * Returns: %TRUE if the node has the given value, %FALSE otherwise */ gboolean ipatch_xml_test_value(GNode *node, const char *cmpvalue) { const char *value; g_return_val_if_fail(node != NULL, FALSE); g_return_val_if_fail(cmpvalue != NULL, FALSE); value = ipatch_xml_get_value(node); return (value && strcmp(value, cmpvalue) == 0); } /** * ipatch_xml_set_attribute: (skip) * @node: XML node * @attr_name: Attribute name to assign to * @attr_value: (nullable): Attribute value to assign or %NULL to unset * * Set or unset an attribute of an XML node. If there is already an existing * attribute with the given @attr_name, its value will be replaced. */ void ipatch_xml_set_attribute(GNode *node, const char *attr_name, const char *attr_value) { IpatchXmlNode *xmlnode; IpatchXmlAttr *attr; GList *p; g_return_if_fail(node != NULL); g_return_if_fail(attr_name != NULL); xmlnode = (IpatchXmlNode *)(node->data); for(p = xmlnode->attributes; p; p = p->next) { attr = (IpatchXmlAttr *)(p->data); if(strcmp(attr->name, attr_name) == 0) { if(attr_value) { g_free(attr->value); attr->value = g_strdup(attr_value); } else { ipatch_xml_attr_free(attr); xmlnode->attributes = g_list_delete_link(xmlnode->attributes, p); } return; } } attr = ipatch_xml_attr_new(); attr->name = g_strdup(attr_name); attr->value = g_strdup(attr_value); xmlnode->attributes = g_list_append(xmlnode->attributes, attr); } /** * ipatch_xml_set_attributes: (skip) * @node: XML node * @attr_name: First attribute name * @attr_value: First attribute value * @...: Additional name/value attribute strings, terminated by a %NULL name * * Set one or more attributes of an XML node. */ void ipatch_xml_set_attributes(GNode *node, const char *attr_name, const char *attr_value, const char *attr2_name, ...) { va_list var_args; char *vname; g_return_if_fail(node != NULL); g_return_if_fail(attr_name != NULL); ipatch_xml_set_attribute(node, attr_name, attr_value); if(!attr2_name) { return; } va_start(var_args, attr2_name); ipatch_xml_set_attribute(node, attr2_name, va_arg(var_args, char *)); while((vname = va_arg(var_args, char *))) { ipatch_xml_set_attribute(node, vname, va_arg(var_args, char *)); } va_end(var_args); } /** * ipatch_xml_get_attribute: (skip) * @node: XML node * @attr_name: Name of attribute * * Get the value of an attribute of an XML node. * * Returns: Value of the named attribute or %NULL if not found, value is internal * and should not be modified or freed. */ G_CONST_RETURN char * ipatch_xml_get_attribute(GNode *node, const char *attr_name) { IpatchXmlAttr *attr; GList *p; g_return_val_if_fail(node != NULL, NULL); g_return_val_if_fail(attr_name != NULL, NULL); for(p = ((IpatchXmlNode *)(node->data))->attributes; p; p = p->next) { attr = (IpatchXmlAttr *)(p->data); if(strcmp(attr->name, attr_name) == 0) { return (attr->value); } } return (NULL); } /** * ipatch_xml_test_attribute: (skip) * @node: XML node * @attr_name: Name of attribute * @cmpval: Value to compare attribute to (%NULL to just check existence). * * Test if an attribute of an XML node is a given value or exists (@cmpval = %NULL). * * Returns: %TRUE if attribute exists and matches @cmpval (if set). */ gboolean ipatch_xml_test_attribute(GNode *node, const char *attr_name, const char *cmpval) { const char *attr_val; g_return_val_if_fail(node != NULL, FALSE); g_return_val_if_fail(attr_name != NULL, FALSE); attr_val = ipatch_xml_get_attribute(node, attr_name); return (attr_val && (!cmpval || strcmp(attr_val, cmpval) == 0)); } /** * ipatch_xml_find_child: (skip) * @node: XML node * @name: Node name of child to find * * Find a child node with the given @name. Only searches the children of @node * and does not search recursively. * * Returns: (transfer none): Matching node or %NULL if not found. */ GNode * ipatch_xml_find_child(GNode *node, const char *name) { IpatchXmlNode *xmlnode; GNode *n; g_return_val_if_fail(node != NULL, NULL); g_return_val_if_fail(name != NULL, NULL); for(n = node->children; n; n = n->next) { xmlnode = (IpatchXmlNode *)(n->data); if(strcmp(xmlnode->name, name) == 0) { return (n); } } return (NULL); } /** * ipatch_xml_find_by_path: (skip) * @node: XML node * @path: Path specification in the form "name1.name2.name3" where each child * of a node is separated by a '.' character. * * Get a node in a tree from a path string. * * Returns: (transfer none): Matching node or %NULL if not found. */ GNode * ipatch_xml_find_by_path(GNode *node, const char *path) { g_return_val_if_fail(node != NULL, NULL); g_return_val_if_fail(path != NULL, NULL); return (ipatch_xml_find_by_path_recurse(node, path)); } static GNode * ipatch_xml_find_by_path_recurse(GNode *node, const char *path) { IpatchXmlNode *xmlnode; char *dot; int len; GNode *n; dot = strchr(path, '.'); len = dot ? dot - path : strlen(path); for(n = node->children; n; n = n->next) { xmlnode = (IpatchXmlNode *)(n->data); if(strncmp(xmlnode->name, path, len) == 0) { if(!dot) { return (n); } else { return (ipatch_xml_find_by_path_recurse(n, dot + 1)); } } } return (NULL); } /** * ipatch_xml_to_str: (skip) * @node: XML node * @indent: Number of spaces of indent per level (0 for no indent) * * Render an XML tree to a string. * * Returns: Newly allocated string of XML content representing @node, free with * g_free() when done using it. */ char * ipatch_xml_to_str(GNode *node, guint indent) { GString *str; g_return_val_if_fail(node != NULL, NULL); str = g_string_new(""); ipatch_xml_to_str_recurse(str, node, 0, indent); return (g_string_free(str, FALSE)); } static void ipatch_xml_to_str_recurse(GString *str, GNode *node, guint indent, guint inc) { IpatchXmlNode *xmlnode; char *esc; GNode *n; guint i; xmlnode = (IpatchXmlNode *)(node->data); for(i = 0; i < indent; i++) { g_string_append_c(str, ' '); } g_string_append_printf(str, "<%s", xmlnode->name); if(xmlnode->attributes) { IpatchXmlAttr *attr; GList *p; for(p = xmlnode->attributes; p; p = p->next) { attr = (IpatchXmlAttr *)(p->data); esc = g_markup_escape_text(attr->value, -1); /* ++ alloc */ g_string_append_printf(str, " %s=\"%s\"", attr->name, esc); g_free(esc); /* -- free */ } } if(!xmlnode->value && !node->children) { g_string_append(str, "/>\n"); return; } else { g_string_append(str, ">"); } if(xmlnode->value) { esc = g_markup_escape_text(xmlnode->value, -1); /* ++ alloc */ g_string_append(str, esc); g_free(esc); /* -- free */ } if(node->children) { g_string_append_c(str, '\n'); for(n = node->children; n; n = n->next) { ipatch_xml_to_str_recurse(str, n, indent + inc, inc); } for(i = 0; i < indent; i++) { g_string_append_c(str, ' '); } } g_string_append_printf(str, "\n", xmlnode->name); } /** * ipatch_xml_save_to_file: (skip) * @node: XML tree to save * @indent: Number of spaces to indent per level * @filename: File name to save to * @err: Location to store error info or %NULL to ignore * * Store an XML tree to a file. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set) */ gboolean ipatch_xml_save_to_file(GNode *node, guint indent, const char *filename, GError **err) { gboolean retval; char *s; s = ipatch_xml_to_str(node, indent); /* ++ alloc */ if(!s) { return (FALSE); } retval = g_file_set_contents(filename, s, -1, err); g_free(s); /* -- free */ return (retval); } /** * ipatch_xml_from_str: (skip) * @str: XML content to parse * @err: Location to store error info or %NULL to ignore * * Parse XML content into an XML node tree. * * Returns: Newly allocated XML node tree or * %NULL on error (@err may be set), can be freed with ipatch_xml_destroy(). */ GNode * ipatch_xml_from_str(const char *str, GError **err) { GMarkupParseContext *ctx; GMarkupParser parser = { xml_start_element, xml_end_element, xml_text }; GNode *root = NULL; ctx = g_markup_parse_context_new(&parser, 0, &root, NULL); if(!g_markup_parse_context_parse(ctx, str, -1, err) || !g_markup_parse_context_end_parse(ctx, err)) { g_markup_parse_context_free(ctx); if(root) { root = g_node_get_root(root); ipatch_xml_destroy(root); } return (NULL); } g_markup_parse_context_free(ctx); return (root); } static void xml_start_element(GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error) { GNode **node = (GNode **)user_data; *node = ipatch_xml_new_node_strv(*node, element_name, NULL, attribute_names, attribute_values); } static void xml_end_element(GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { GNode **node = (GNode **)user_data; if((*node)->parent) { *node = (*node)->parent; } } static void xml_text(GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error) { GNode **node = (GNode **)user_data; IpatchXmlNode *xmlnode; xmlnode = (IpatchXmlNode *)((*node)->data); g_free(xmlnode->value); xmlnode->value = g_strdup(text); } /** * ipatch_xml_load_from_file: (skip) * @filename: File name containing XML content to parse * @err: Location to store error info or %NULL to ignore * * Parse an XML file into an XML node tree. * * Returns: Newly allocated XML node tree * or %NULL on error (@err may be set), can be freed with ipatch_xml_destroy(). */ GNode * ipatch_xml_load_from_file(const char *filename, GError **err) { GNode *node; char *str; g_return_val_if_fail(filename != NULL, NULL); g_return_val_if_fail(!err || !*err, NULL); if(!g_file_get_contents(filename, &str, NULL, err)) /* ++ alloc */ { return (NULL); } node = ipatch_xml_from_str(str, err); g_free(str); return (node); } /** * ipatch_xml_node_new: (skip) * * Create a new XML node structure. Not normally used. * * Returns: New XML node structure, which should be added to a GNode. */ IpatchXmlNode * ipatch_xml_node_new(void) { IpatchXmlNode *xmlnode; xmlnode = g_slice_new0(IpatchXmlNode); g_datalist_init(&xmlnode->qdata); return (xmlnode); } /** * ipatch_xml_node_free: (skip) * @xmlnode: XML node structure to free * * Free an XML node structure and its contents. Not normally used. */ void ipatch_xml_node_free(IpatchXmlNode *xmlnode) { GList *p; g_return_if_fail(xmlnode != NULL); g_free(xmlnode->name); g_free(xmlnode->value); g_datalist_clear(&xmlnode->qdata); for(p = xmlnode->attributes; p; p = g_list_delete_link(p, p)) { ipatch_xml_attr_free(p->data); } g_slice_free(IpatchXmlNode, xmlnode); } /** * ipatch_xml_node_duplicate: (skip) * @xmlnode: XML node structure to duplicate * * Duplicate an XML node structure and its contents. Not normally used. * Note that arbitrary user data assigned to the XML node will not be duplicated. * * Returns: New duplicate of @xmlnode. */ IpatchXmlNode * ipatch_xml_node_duplicate(const IpatchXmlNode *xmlnode) { IpatchXmlNode *dupnode; IpatchXmlAttr *dupattr; GList *p; g_return_val_if_fail(xmlnode != NULL, NULL); dupnode = ipatch_xml_node_new(); dupnode->name = g_strdup(xmlnode->name); dupnode->value = g_strdup(xmlnode->value); for(p = xmlnode->attributes; p; p = p->next) { dupattr = ipatch_xml_attr_duplicate(p->data); dupnode->attributes = g_list_prepend(dupnode->attributes, dupattr); } dupnode->attributes = g_list_reverse(dupnode->attributes); return (dupnode); } /** * ipatch_xml_attr_new: (skip) * * Create a new XML attribute structure. Not normally used. * * Returns: New XML attribute structure which should be added to an #IpatchXmlNode * attributes list. */ IpatchXmlAttr * ipatch_xml_attr_new(void) { return (g_slice_new0(IpatchXmlAttr)); } /** * ipatch_xml_attr_free: (skip) * @attr: Attribute structure to free * * Free an XML attribute structure. Not normally used. */ void ipatch_xml_attr_free(IpatchXmlAttr *attr) { g_return_if_fail(attr != NULL); g_free(attr->name); g_free(attr->value); g_slice_free(IpatchXmlAttr, attr); } /** * ipatch_xml_attr_duplicate: (skip) * @attr: Attribute structure to duplicate * * Duplicate an XML attribute structure. Not normally used. * * Returns: New duplicate attribute structure */ IpatchXmlAttr * ipatch_xml_attr_duplicate(const IpatchXmlAttr *attr) { IpatchXmlAttr *dupattr; g_return_val_if_fail(attr != NULL, NULL); dupattr = ipatch_xml_attr_new(); dupattr->name = g_strdup(attr->name); dupattr->value = g_strdup(attr->value); return (dupattr); } libinstpatch-1.1.6/libinstpatch/IpatchXml.h000066400000000000000000000106411400263525300207330ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_XML_H__ #define __IPATCH_XML_H__ #include typedef struct _IpatchXmlNode IpatchXmlNode; typedef struct _IpatchXmlAttr IpatchXmlAttr; /** * IpatchXmlAttr: * @name: Attribute name * @value: Attribute value * * Structure for storing an XML attribute. */ struct _IpatchXmlAttr { char *name; char *value; }; /** * IpatchXmlNode: * @name: XML element name * @value: XML text value or %NULL * @attributes: Linked list of #IpatchXmlAttr structures * * An XML element node. Note that a given node can contain only one text value. */ struct _IpatchXmlNode { char *name; /* XML element name */ char *value; /* Text content of element */ GData *qdata; /* To associate arbitrary data with XML nodes */ GList *attributes; /* List of IpatchXmlAttr structures */ }; GNode *ipatch_xml_new_node(GNode *parent, const char *name, const char *value, const char *attr_name, ...); GNode *ipatch_xml_new_node_strv(GNode *parent, const char *name, const char *value, const char **attr_names, const char **attr_values); gpointer ipatch_xml_get_data(GNode *node, const char *key); void ipatch_xml_set_data(GNode *node, const char *key, gpointer data); void ipatch_xml_set_data_full(GNode *node, const char *key, gpointer data, GDestroyNotify destroy_func); gpointer ipatch_xml_steal_data(GNode *node, const char *key); gpointer ipatch_xml_get_qdata(GNode *node, GQuark quark); void ipatch_xml_set_qdata(GNode *node, GQuark quark, gpointer data); void ipatch_xml_set_qdata_full(GNode *node, GQuark quark, gpointer data, GDestroyNotify destroy_func); gpointer ipatch_xml_steal_qdata(GNode *node, GQuark quark); void ipatch_xml_destroy(GNode *node); GNode *ipatch_xml_copy(GNode *node); void ipatch_xml_set_name(GNode *node, const char *name); void ipatch_xml_set_value(GNode *node, const char *value); void ipatch_xml_set_value_printf(GNode *node, const char *format, ...); void ipatch_xml_take_name(GNode *node, char *name); void ipatch_xml_take_value(GNode *node, char *value); G_CONST_RETURN char *ipatch_xml_get_name(GNode *node); gboolean ipatch_xml_test_name(GNode *node, const char *cmpname); G_CONST_RETURN char *ipatch_xml_get_value(GNode *node); char *ipatch_xml_dup_value(GNode *node); gboolean ipatch_xml_test_value(GNode *node, const char *cmpvalue); void ipatch_xml_set_attribute(GNode *node, const char *attr_name, const char *attr_value); void ipatch_xml_set_attributes(GNode *node, const char *attr_name, const char *attr_value, const char *attr2_name, ...); G_CONST_RETURN char *ipatch_xml_get_attribute(GNode *node, const char *attr_name); gboolean ipatch_xml_test_attribute(GNode *node, const char *attr_name, const char *cmpval); GNode *ipatch_xml_find_child(GNode *node, const char *name); GNode *ipatch_xml_find_by_path(GNode *node, const char *path); char *ipatch_xml_to_str(GNode *node, guint indent); gboolean ipatch_xml_save_to_file(GNode *node, guint indent, const char *filename, GError **err); GNode *ipatch_xml_from_str(const char *str, GError **err); GNode *ipatch_xml_load_from_file(const char *filename, GError **err); IpatchXmlNode *ipatch_xml_node_new(void); void ipatch_xml_node_free(IpatchXmlNode *xmlnode); IpatchXmlNode *ipatch_xml_node_duplicate(const IpatchXmlNode *xmlnode); IpatchXmlAttr *ipatch_xml_attr_new(void); void ipatch_xml_attr_free(IpatchXmlAttr *attr); IpatchXmlAttr *ipatch_xml_attr_duplicate(const IpatchXmlAttr *attr); #endif libinstpatch-1.1.6/libinstpatch/IpatchXmlObject.c000066400000000000000000001154051400263525300220610ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: IpatchXmlObject * @short_description: GObject encoding/decoding related XML tree functions * @see_also: IpatchXml * @stability: Stable * * @introduction * This module provides functions for saving/loading whole GObject properties, * single GObject property and single GValue to/from XML trees. * * The module includes also a system for registering custom encoding * and decoding handlers for objects properties, single property and single Galue * types (see 1). * Custom XML encoding handler will be used when saving to XML trees (see 2). * Conversely custom XML decoding handler will be used when loading from XML * trees (see 3). * * @Functions presentation 1) registering system for encoding/decoding handlers: * 1.1)First we create an internal table (xml_handlers) that will be used to * register custom encoding/decoding XML handlers. This table is created by * _ipatch_xml_object_init() during libinstpatch initialization (ipatch_init()) * and is owned by libinstpatch. * * 1.2)Then if the application need custom encoding/decoding XML handlers it * must call ipatch_xml_register_handler(). Once handlers are registered, they * will be called automatically when the application will use functions to save * (see 2) or load (see 3) gobjet properties, a single property or a single * GValue to/from XML tree. Note that if a custom handlers doesn't exist a * default handler will be used instead. * * 1.3)Any custom handlers may be looked by calling patch_xml_lookup_handler(), * patch_xml_lookup_handler_by_prop_name() * * 2)Functions for encoding (saving) whole object properties, single GObject * property or single GValue to an XML tree node: * * 2.1) To save a whole object to an XML tree node, the application must call * ipatch_xml_encode_object(node, object). It is maily intended to save many * properties values belonging to the given object. Which properties are saved * depends of the behaviour of custom encoding handlers registered (if any). * Default encoding handler save all properties's value (except those which * have the #IPATCH_PARAM_NO_SAVE flag set. * * 2.1) To save a single property object to an XML tree node, the application * must call ipatch_xml_encode_property(node, object, pspec) or * ipatch_xml_encode_property_by_name(node, object, propname). * * 2.2) To save a single GValue value to an XML tree node, the application * must call ipatch_xml_encode_value (node, value). * * 3)Functions for decoding (loading) whole object, single GObject property or * single GValue from an XML tree node: * * 3.1) To load a whole object from an XML tree node, the application must call * ipatch_xml_decode_object(node, object). It is maily intended to load many * properties values belonging to the given object. Which properties are loaded * depends of the behaviour of custom decoding handlers registered (if any). * Default decoding handler load all properties's value (except those which * have the #IPATCH_PARAM_NO_SAVE flag set. * * 3.1) To load a single property object from an XML tree node, the application * must call ipatch_xml_decode_property(node, object, pspec) or * ipatch_xml_decode_property_by_name(node, object, propname). * * 3.2) To load a single GValue value from an XML tree node, the application * must call ipatch_xml_decode_value (node, value). */ #include "config.h" #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_LOCALE_H #include #endif #ifdef HAVE_XLOCALE_H #include #endif #include "IpatchXmlObject.h" #include "IpatchXml.h" #include "IpatchParamProp.h" #include "misc.h" #include "i18n.h" /*-----(1) registering system for custom encoding/decoding handlers:---------*/ /* Number of decimal places of precision for floating point numbers stored to XML */ #define XML_FLOAT_PRECISION 6 /* structure used as hash key for xml_handlers */ typedef struct { GType type; GParamSpec *pspec; } HandlerHashKey; /* structure used as hash value for xml_handlers */ typedef struct { IpatchXmlEncodeFunc encode_func; IpatchXmlDecodeFunc decode_func; GDestroyNotify notify_func; gpointer user_data; } HandlerHashValue; static guint xml_handlers_hash_func(gconstpointer key); static gboolean xml_handlers_key_equal_func(gconstpointer a, gconstpointer b); static void xml_handlers_key_destroy_func(gpointer data); static void xml_handlers_value_destroy_func(gpointer data); /* Lock for xml_handlers */ G_LOCK_DEFINE_STATIC(xml_handlers); /* hash of XML handlers (HandlerHashKey -> HandlerHashValue) */ static GHashTable *xml_handlers = NULL; /*----------------------------------------------------------------------------- 1) Initialization/deinitialization of loading/saving system ----------------------------------------------------------------------------*/ /* * Initialize IpatchXmlObject loading/saving system */ void _ipatch_xml_object_init(void) { xml_handlers = g_hash_table_new_full(xml_handlers_hash_func, xml_handlers_key_equal_func, xml_handlers_key_destroy_func, xml_handlers_value_destroy_func); } /* hash function for buiding hash key for xml_handlers */ /* hash key is a combination of GParamSpec property and GType */ static guint xml_handlers_hash_func(gconstpointer key) { HandlerHashKey *hkey = (HandlerHashKey *)key; return (hkey->type + GPOINTER_TO_UINT(hkey->pspec)); } /* check equal key for hash table */ /* return TRUE when key a is equal to key b */ static gboolean xml_handlers_key_equal_func(gconstpointer a, gconstpointer b) { HandlerHashKey *akey = (HandlerHashKey *)a, *bkey = (HandlerHashKey *)b; return (akey->type == bkey->type && akey->pspec == bkey->pspec); } /* key destroy function */ static void xml_handlers_key_destroy_func(gpointer data) { g_slice_free(HandlerHashKey, data); } /* value destroy function */ static void xml_handlers_value_destroy_func(gpointer data) { g_slice_free(HandlerHashValue, data); } /* * Free IpatchXmlObject loading/saving system */ void _ipatch_xml_object_deinit(void) { g_hash_table_destroy(xml_handlers); } /** * ipatch_xml_register_handler: (skip) * @type: GType to register handler functions for (GObject type if an object or * object property handler, GValue type for value handlers). * @prop_name: GObject property name (or %NULL if not a GObject property handler) * @encode_func: Function to handle encoding (object/property/value -> XML) * @decode_func: Function to handle decoding (XML -> object/property/value) * * Registers XML encoding/decoding handlers for a GObject type, GObject property or * GValue type. */ void ipatch_xml_register_handler(GType type, const char *prop_name, IpatchXmlEncodeFunc encode_func, IpatchXmlDecodeFunc decode_func) { ipatch_xml_register_handler_full(type, prop_name, encode_func, decode_func, NULL, NULL); } /** * ipatch_xml_register_handler_full: (rename-to ipatch_xml_register_handler) * @type: GType to register handler functions for (GObject type if an object or * object property handler, GValue type for value handlers). * @prop_name: (nullable): GObject property name (or %NULL if not a GObject property handler) * @encode_func: (scope notified): Function to handle encoding (object/property/value -> XML) * @decode_func: (scope notified): Function to handle decoding (XML -> object/property/value) * @notify_func: (nullable) (scope async) (closure user_data): Callback when handlers are removed. * @user_data: (nullable): Data passed to @notify_func * * Registers XML encoding/decoding handlers for a GObject type, GObject property or * GValue type. * * Since: 1.1.0 */ void ipatch_xml_register_handler_full(GType type, const char *prop_name, IpatchXmlEncodeFunc encode_func, IpatchXmlDecodeFunc decode_func, GDestroyNotify notify_func, gpointer user_data) { HandlerHashKey *key; HandlerHashValue *val; GParamSpec *pspec = NULL; GObjectClass *obj_class; g_return_if_fail(type != 0); g_return_if_fail(encode_func != NULL); g_return_if_fail(decode_func != NULL); /* get GParamSpec property of GObject */ if(prop_name) { obj_class = g_type_class_peek(type); g_return_if_fail(obj_class != NULL); pspec = g_object_class_find_property(obj_class, prop_name); g_return_if_fail(pspec != NULL); } /* alloc memory for hash key */ key = g_slice_new(HandlerHashKey); key->type = type; key->pspec = pspec; /* alloc memory for hash value */ val = g_slice_new(HandlerHashValue); val->encode_func = encode_func; /* object or property or GValue -> XML */ val->decode_func = decode_func; /* XML -> object or property or GValue */ val->notify_func = notify_func; /* Callback when handlers are removed */ val->user_data = user_data; /* data passed to notify func */ /* put value in hash table */ G_LOCK(xml_handlers); g_hash_table_insert(xml_handlers, key, val); G_UNLOCK(xml_handlers); } /** * ipatch_xml_lookup_handler: (skip) * @type: GObject or GValue type of handler to lookup * @pspec: (nullable): GObject property spec (or %NULL if not a GObject property handler) * @encode_func: (out) (optional): Location to store encoding function (or %NULL to ignore) * @decode_func: (out) (optional): Location to store decoding function (or %NULL to ignore) * * Looks up handlers for a given GObject type, GObject property or GValue * type previously registered with ipatch_xml_register_handler(). * These functions are used for encoding/decoding to/from XML. * * Returns: %TRUE if handler found, %FALSE otherwise */ gboolean ipatch_xml_lookup_handler(GType type, GParamSpec *pspec, IpatchXmlEncodeFunc *encode_func, IpatchXmlDecodeFunc *decode_func) { HandlerHashValue *val; HandlerHashKey key; g_return_val_if_fail(type != 0, FALSE); /* prepare the hash key */ key.type = type; key.pspec = pspec; /* get the xml handlers from this key */ G_LOCK(xml_handlers); val = g_hash_table_lookup(xml_handlers, &key); G_UNLOCK(xml_handlers); /* return the encode and decode handlers if requested */ if(encode_func) { *encode_func = val ? val->encode_func : NULL; } if(decode_func) { *decode_func = val ? val->decode_func : NULL; } /* return True if any handlers exists */ return (val != NULL); } /** * ipatch_xml_lookup_handler_by_prop_name: (skip) * @type: GObject or GValue type of handler to lookup * @prop_name: (nullable): GObject property name (or %NULL if not a GObject property handler) * @encode_func: (out) (optional): Location to store encoding function (or %NULL to ignore) * @decode_func: (out) (optional): Location to store decoding function (or %NULL to ignore) * * Like ipatch_xml_lookup_handler() but takes a @prop_name string to indicate which * GObject property to lookup instead of a GParamSpec. * * Returns: %TRUE if handler found, %FALSE otherwise */ gboolean ipatch_xml_lookup_handler_by_prop_name(GType type, const char *prop_name, IpatchXmlEncodeFunc *encode_func, IpatchXmlDecodeFunc *decode_func) { GParamSpec *pspec = NULL; GObjectClass *obj_class; g_return_val_if_fail(type != 0, FALSE); /* get GParamSpec property of GObject */ if(prop_name) { obj_class = g_type_class_peek(type); g_return_val_if_fail(obj_class != NULL, FALSE); pspec = g_object_class_find_property(obj_class, prop_name); g_return_val_if_fail(pspec != NULL, FALSE); } /* get the handler */ return (ipatch_xml_lookup_handler(type, pspec, encode_func, decode_func)); } /* (2) function to encode (save) whole object, a single property, or a single GValue value to an XML tree node */ /* IpatchXmlCodecFuncLocale is a pointer on IpatchXmDecodeFunc or IpatchXmlEncodeFunc function */ typedef IpatchXmlEncodeFunc IpatchXmlCodecFuncLocale; /* Call codec function with parameters node, object, pspec, value, err. Before calling codec, the current task locale is set to LC_NUMERIC to ensure that numeric value are properly coded/decoded using ipatch_xml_xxxx_decode_xxxx_func(). This will ensure that when decoding float numbers, decimal part values are properly decoded. Otherwise there is risk that decimal part will be ignored, leading in previous float preferences being read as integer value. On return, the locale is restored to the value it had before calling the function. */ static gboolean ipatch_xml_codec_func_locale(IpatchXmlCodecFuncLocale codec, GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err) { gboolean retval; /* save the current task locale and set the needed task locale */ #ifdef WIN32 char* oldLocale; int oldSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); g_return_val_if_fail(oldSetting != -1, FALSE); oldLocale = setlocale(LC_NUMERIC, NULL); g_return_val_if_fail(setlocale(LC_NUMERIC, "") != NULL, FALSE); #else locale_t oldLocale; locale_t newLocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0); g_return_val_if_fail(newLocale != (locale_t) 0, FALSE); oldLocale = uselocale(newLocale); g_return_val_if_fail(oldLocale != (locale_t) 0, FALSE); #endif /* call the encode or decode function */ retval = codec(node, object, pspec, value, err); #ifdef WIN32 /* restore the locale */ setlocale(LC_NUMERIC, oldLocale); _configthreadlocale(oldSetting); #else /* restore the locale */ uselocale(oldLocale); freelocale(newLocale); #endif return retval; } /** * ipatch_xml_encode_object: * @node: XML node to encode to * @object: Object to encode to XML * @create_element: %TRUE to create a <obj> element, %FALSE to add object * properties to current open element * @err: Location to store error info or %NULL to ignore * * Encodes an object to XML. It there is no encoder for this object a default * encoder ipatch_xml_default_encode_object_func() will be used instead. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_xml_encode_object(GNode *node, GObject *object, gboolean create_element, GError **err) { IpatchXmlEncodeFunc encode_func; GType type; g_return_val_if_fail(node != NULL, FALSE); g_return_val_if_fail(G_IS_OBJECT(object), FALSE); g_return_val_if_fail(!err || !*err, FALSE); type = G_OBJECT_TYPE(object); /* search through type ancestry for Object handler */ do { if(ipatch_xml_lookup_handler(type, NULL, &encode_func, NULL)) { break; } } while((type = g_type_parent(type))); /* not found? Use default Object encoder */ if(!type) { encode_func = ipatch_xml_default_encode_object_func; } /* create a new XML node element if requested */ if(create_element) node = ipatch_xml_new_node(node, "obj", NULL, "type", g_type_name(type), NULL); /* encode all object'properties in XML node */ return ipatch_xml_codec_func_locale(encode_func, node, object, NULL, NULL, err); } /** * ipatch_xml_encode_property: * @node: XML node to encode to * @object: GObject to encode property of * @pspec: Parameter specification of property to encode * @create_element: %TRUE to create a <prop name="PROPNAME"> element, %FALSE to * assign object property value to node * @err: Location to store error info or %NULL to ignore * * Encode an object property value to an XML node. * If there is no encoder for this property, the function * ipatch_xml_encode_value() will be used instead. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean ipatch_xml_encode_property(GNode *node, GObject *object, GParamSpec *pspec, gboolean create_element, GError **err) { IpatchXmlEncodeFunc encode_func; GValue value = { 0 }; gboolean retval; g_return_val_if_fail(node != NULL, FALSE); g_return_val_if_fail(G_IS_OBJECT(object), FALSE); g_return_val_if_fail(G_IS_PARAM_SPEC(pspec), FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* ++ alloc value */ g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec)); /* get property value */ g_object_get_property(object, g_param_spec_get_name(pspec), &value); /* create a new XML node element if requested */ if(create_element) { node = ipatch_xml_new_node(node, "prop", NULL, "name", pspec->name, NULL); } /* looking for the property's xml encoder */ if(!ipatch_xml_lookup_handler(pspec->owner_type, pspec, &encode_func, NULL)) { /* Use default property encoder when handler not found */ retval = ipatch_xml_encode_value(node, &value, err); } else { /* Use handler encoder */ retval = ipatch_xml_codec_func_locale(encode_func, node, object, pspec, &value, err); } g_value_unset(&value); /* -- free value */ if(!retval && create_element) { ipatch_xml_destroy(node); /* Cleanup after error (if create_element) */ } return (retval); } /** * ipatch_xml_encode_property_by_name: * @node: XML node to encode to * @object: GObject to encode property of * @propname: Name of object property to encode * @create_element: %TRUE to create a <prop name="PROPNAME"> element, %FALSE to * assign object property value to node * @err: Location to store error info or %NULL to ignore * * Encode an object property by name to an XML node. * Like ipatch_xml_encode_property() but takes a @propname string instead * of a GParamSpec. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean ipatch_xml_encode_property_by_name(GNode *node, GObject *object, const char *propname, gboolean create_element, GError **err) { GParamSpec *pspec; g_return_val_if_fail(node != NULL, FALSE); g_return_val_if_fail(G_IS_OBJECT(object), FALSE); g_return_val_if_fail(propname != NULL, FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* looking for object's GParamSpec property */ pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(object), propname); g_return_val_if_fail(pspec != NULL, FALSE); /* Encode object's property value to the XML node. */ return (ipatch_xml_encode_property(node, object, pspec, create_element, err)); } /** * ipatch_xml_encode_value: * @node: XML node to encode to * @value: Value to encode * @err: Location to store error info or %NULL to ignore * * Encodes a GValue to an XML node text value. If there is not * encoder for this GValue, ipatch_xml_default_encode_value_func() * default encoder will be used instead. * * Returns: TRUE on success, FALSE on error (@err may be set) */ gboolean ipatch_xml_encode_value(GNode *node, GValue *value, GError **err) { IpatchXmlEncodeFunc encode_func; g_return_val_if_fail(node != NULL, FALSE); g_return_val_if_fail(G_IS_VALUE(value), FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* jjc looking for the GValue xml encoder */ if(!ipatch_xml_lookup_handler(G_VALUE_TYPE(value), NULL, &encode_func, NULL)) { encode_func = ipatch_xml_default_encode_value_func; } /* Encode GValue to the XML node. */ return ipatch_xml_codec_func_locale(encode_func, node, NULL, NULL, value, err); } /* (3) function to decode (load) whole object, a single proprety, or a single GValue value from an xml tree node: */ /** * ipatch_xml_decode_object: * @node: XML node to decode from * @object: Object to decode to from XML * @err: Location to store error info or %NULL to ignore * * Decodes XML to an object. If there is no decoder for this object, * the default GObject decoder ipatch_xml_default_decode_object_func() * will be used and will only decode those properties which don't have the * #IPATCH_PARAM_NO_SAVE flag set. * * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set). */ gboolean ipatch_xml_decode_object(GNode *node, GObject *object, GError **err) { IpatchXmlDecodeFunc decode_func; GType type; g_return_val_if_fail(node != NULL, FALSE); g_return_val_if_fail(G_IS_OBJECT(object), FALSE); g_return_val_if_fail(!err || !*err, FALSE); type = G_OBJECT_TYPE(object); /* search through type ancestry for Object handler */ do { if(ipatch_xml_lookup_handler(type, NULL, NULL, &decode_func)) { break; } } while((type = g_type_parent(type))); /* not found? Use default Object decoder */ if(!type) { decode_func = ipatch_xml_default_decode_object_func; } /* decode XML node to object. */ return ipatch_xml_codec_func_locale(decode_func, node, object, NULL, NULL, err); } /** * ipatch_xml_decode_property: * @node: XML node to decode from * @object: GObject to decode property of * @pspec: Parameter specification of property to decode * @err: Location to store error info or %NULL to ignore * * Decode an object property from an XML node value to an object. If there is * no decoder for this property, ipatch_xml_decode_value() function will be * used instead. * The property is NOT checked for the #IPATCH_PARAM_NO_SAVE flag. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean ipatch_xml_decode_property(GNode *node, GObject *object, GParamSpec *pspec, GError **err) { IpatchXmlDecodeFunc decode_func; GValue value = { 0 }; gboolean retval; g_return_val_if_fail(node != NULL, FALSE); g_return_val_if_fail(G_IS_OBJECT(object), FALSE); g_return_val_if_fail(G_IS_PARAM_SPEC(pspec), FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* ++ alloc value */ g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec)); /* looking for the property xml decoder */ if(!ipatch_xml_lookup_handler(pspec->owner_type, pspec, NULL, &decode_func)) { retval = ipatch_xml_decode_value(node, &value, err); } else { retval = ipatch_xml_codec_func_locale(decode_func, node, object, pspec, &value, err); } /* set the decoded property value */ if(retval) { g_object_set_property(object, pspec->name, &value); } g_value_unset(&value); /* -- free value */ return (retval); } /** * ipatch_xml_decode_property_by_name: * @node: XML node to decode from * @object: GObject to decode property of * @propname: Name of object property to decode * @err: Location to store error info or %NULL to ignore * * Decode an object property from an XML node value to an object by property name. * Like ipatch_xml_decode_property() but but takes a @propname string instead * of a GParamSpec. * Note that the property is NOT checked for the #IPATCH_PARAM_NO_SAVE flag. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean ipatch_xml_decode_property_by_name(GNode *node, GObject *object, const char *propname, GError **err) { GParamSpec *pspec; g_return_val_if_fail(node != NULL, FALSE); g_return_val_if_fail(G_IS_OBJECT(object), FALSE); g_return_val_if_fail(propname != NULL, FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* looking for object's property */ pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(object), propname); g_return_val_if_fail(pspec != NULL, FALSE); /* Decode the object property from the XML node */ return (ipatch_xml_decode_property(node, object, pspec, err)); } /** * ipatch_xml_decode_value: * @node: XML node to decode from * @value: Value to decode to * @err: Location to store error info or %NULL to ignore * * Decodes a GValue from an XML node text value. * * Returns: TRUE on success, FALSE on error (@err may be set) */ gboolean ipatch_xml_decode_value(GNode *node, GValue *value, GError **err) { IpatchXmlDecodeFunc decode_func; g_return_val_if_fail(node != NULL, FALSE); g_return_val_if_fail(G_IS_VALUE(value), FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* jjc looking from GValue decoder */ if(!ipatch_xml_lookup_handler(G_VALUE_TYPE(value), NULL, NULL, &decode_func)) { decode_func = ipatch_xml_default_decode_value_func; } /* Decode a GValue from an XML node text value. */ return ipatch_xml_codec_func_locale(decode_func, node, NULL, NULL, value, err); } /*------------------- default XML encoder handlers ------------------------------*/ /** * ipatch_xml_default_encode_object_func: (type IpatchXmlEncodeFunc) * @node: XML node to encode XML to * @object: Object to encode * @pspec: Will be %NULL * @value: Will be %NULL * @err: Location to store error value (or %NULL if ignoring) * * Default GObject encode handler. Useful to chain to the default if custom * object handler doesn't exist. All properties belonging to object are encoded * except those which have the * #IPATCH_PARAM_NO_SAVE flag set. * * Returns: TRUE on success, FALSE on error (@err may be set) */ gboolean ipatch_xml_default_encode_object_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err) { GParamSpec **pspecs; /* table of object properties */ GError *local_err = NULL; /* number of properties in pspecs */ guint n_props; guint i; /* get table of object properties */ pspecs = g_object_class_list_properties(G_OBJECT_GET_CLASS(object), /* ++ alloc */ &n_props); for(i = 0; i < n_props; i++) { /* Skip parameters marked as no save or not read/write */ if((pspecs[i]->flags & IPATCH_PARAM_NO_SAVE) || (pspecs[i]->flags & G_PARAM_READWRITE) != G_PARAM_READWRITE) { continue; } /* encode a property value in a new XML node with node beeing parent */ if(!ipatch_xml_encode_property(node, object, pspecs[i], TRUE, &local_err)) /* ++ alloc */ { g_warning("Failed to store property '%s' for object of type '%s': %s", pspecs[i]->name, g_type_name(G_OBJECT_TYPE(object)), ipatch_gerror_message(local_err)); g_clear_error(&local_err); } } g_free(pspecs); /* -- free */ return (TRUE); } // not used. /** * ipatch_xml_default_encode_property_func: (type IpatchXmlEncodeFunc) * @node: XML node to encode XML to * @object: Object to encode * @pspec: Parameter spec of property to encode * @value: Value of the property * @err: Location to store error value (or %NULL if ignoring) * * Default GObject property encode handler. Useful for custom handlers to chain * to the default if needed. * * Returns: TRUE on success, FALSE on error (@err may be set) */ gboolean ipatch_xml_default_encode_property_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err) { return (ipatch_xml_encode_value(node, value, err)); } /** * ipatch_xml_default_encode_value_func: (type IpatchXmlEncodeFunc) * @node: XML node to encode XML to * @object: Will be %NULL * @pspec: Will be %NULL * @value: Value to encode * @err: Location to store error value (or %NULL if ignoring) * * Default GValue encode handler. Useful to chain to the default * when custom GValue encoder doesn't exist. * * Returns: TRUE on success, FALSE on error (@err may be set) */ gboolean ipatch_xml_default_encode_value_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err) { GType valtype; const char *s; g_return_val_if_fail(node != NULL, FALSE); g_return_val_if_fail(G_IS_VALUE(value), FALSE); g_return_val_if_fail(!err || !*err, FALSE); /* get value type */ valtype = G_VALUE_TYPE(value); switch(G_TYPE_FUNDAMENTAL(valtype)) { case G_TYPE_CHAR: ipatch_xml_set_value_printf(node, "%d", g_value_get_char(value)); return (TRUE); case G_TYPE_UCHAR: ipatch_xml_set_value_printf(node, "%u", g_value_get_uchar(value)); return (TRUE); case G_TYPE_BOOLEAN: ipatch_xml_set_value_printf(node, "%u", g_value_get_boolean(value) != 0); return (TRUE); case G_TYPE_INT: ipatch_xml_set_value_printf(node, "%d", g_value_get_int(value)); return (TRUE); case G_TYPE_UINT: ipatch_xml_set_value_printf(node, "%u", g_value_get_uint(value)); return (TRUE); case G_TYPE_LONG: ipatch_xml_set_value_printf(node, "%ld", g_value_get_long(value)); return (TRUE); case G_TYPE_ULONG: ipatch_xml_set_value_printf(node, "%lu", g_value_get_ulong(value)); return (TRUE); case G_TYPE_INT64: ipatch_xml_set_value_printf(node, "%" G_GINT64_FORMAT, g_value_get_int64(value)); return (TRUE); case G_TYPE_UINT64: ipatch_xml_set_value_printf(node, "%" G_GUINT64_FORMAT, g_value_get_uint64(value)); return (TRUE); case G_TYPE_ENUM: ipatch_xml_set_value_printf(node, "%d", g_value_get_enum(value)); return (TRUE); case G_TYPE_FLAGS: ipatch_xml_set_value_printf(node, "%u", g_value_get_flags(value)); return (TRUE); case G_TYPE_FLOAT: ipatch_xml_set_value_printf(node, "%.*f", XML_FLOAT_PRECISION, (double)g_value_get_float(value)); return (TRUE); case G_TYPE_DOUBLE: ipatch_xml_set_value_printf(node, "%.*f", XML_FLOAT_PRECISION, g_value_get_double(value)); return (TRUE); case G_TYPE_STRING: s = g_value_get_string(value); if(s) { ipatch_xml_take_value(node, g_markup_escape_text(s, -1)); } else { ipatch_xml_set_value(node, NULL); } return (TRUE); default: if(valtype == G_TYPE_GTYPE) { ipatch_xml_set_value(node, g_type_name(g_value_get_gtype(value))); return (TRUE); } else { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNHANDLED_CONVERSION, "Unhandled GValue to XML conversion for type '%s'", g_type_name(valtype)); return (FALSE); } } return (TRUE); } /*------------------- default XML decoder handlers ------------------------------*/ /** * ipatch_xml_default_decode_object_func: (type IpatchXmlDecodeFunc) * @node: XML node to decode XML from * @object: Object to decode to * @pspec: Will be %NULL * @value: Will be %NULL * @err: Location to store error value (or %NULL if ignoring) * * Default GObject decode handler. Useful for custom handlers to chain to * the default if needed. * * Returns: TRUE on success, FALSE on error (@err may be set) */ gboolean ipatch_xml_default_decode_object_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err) { IpatchXmlNode *xmlnode; GObjectClass *klass; GParamSpec *prop; const char *propname; GError *local_err = NULL; GNode *n; klass = G_OBJECT_GET_CLASS(object); /* Look for property child nodes */ for(n = node->children; n; n = n->next) { xmlnode = (IpatchXmlNode *)(n->data); /* get property name from child XML node */ if(strcmp(xmlnode->name, "prop") != 0) { continue; } propname = ipatch_xml_get_attribute(n, "name"); if(!propname) { continue; } /* get GParamSpec property */ prop = g_object_class_find_property(klass, propname); if(prop) { /* ignore property marked with IPATCH_PARAM_NO_SAVE flag */ if(prop->flags & IPATCH_PARAM_NO_SAVE) { g_warning(_("Ignoring non storeable XML object property '%s' for object type '%s'"), prop->name, g_type_name(G_OBJECT_TYPE(object))); continue; } /* decode property value from XML node n */ if(!ipatch_xml_decode_property(n, object, prop, &local_err)) { g_warning(_("Failed to decode object property: %s"), ipatch_gerror_message(local_err)); g_clear_error(&local_err); } } else g_warning(_("XML object property '%s' not valid for object type '%s'"), propname, g_type_name(G_OBJECT_TYPE(object))); } return (TRUE); } // not used. /** * ipatch_xml_default_decode_property_func: (type IpatchXmlDecodeFunc) * @node: XML node to decode XML from * @object: Object whose property is being decoded * @pspec: Parameter spec of property to decode * @value: Initialized value to decode to * @err: Location to store error value (or %NULL if ignoring) * * Default GObject property decode handler. Useful to chain to the default * if custom property handler doesn't exist. * * Returns: TRUE on success, FALSE on error (@err may be set) */ gboolean ipatch_xml_default_decode_property_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err) { return (ipatch_xml_decode_value(node, value, err)); } /** * ipatch_xml_default_decode_value_func: (type IpatchXmlDecodeFunc) * @node: XML node to decode XML from * @object: Will be %NULL * @pspec: Will be %NULL * @value: Value to decode to * @err: Location to store error value (or %NULL if ignoring) * * Default GObject GValue decode handler. Useful to chain to the default * if custom GValue handler doesn't exist. * * Returns: TRUE on success, FALSE on error (@err may be set) */ gboolean ipatch_xml_default_decode_value_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err) { GType valtype; guint64 u64; gint64 i64; gulong lu; glong li; guint u; int i; float f; double d; const char *xml; char *endptr; valtype = G_VALUE_TYPE(value); xml = ipatch_xml_get_value(node); if(!xml) { xml = ""; } switch(G_TYPE_FUNDAMENTAL(valtype)) { case G_TYPE_CHAR: if(sscanf(xml, "%d", &i) != 1) { goto malformed_err; } if(i < G_MININT8 || i > G_MAXINT8) { goto range_err; } g_value_set_char(value, i); break; case G_TYPE_UCHAR: if(sscanf(xml, "%u", &u) != 1) { goto malformed_err; } if(u > G_MAXUINT8) { goto range_err; } g_value_set_uchar(value, u); break; case G_TYPE_BOOLEAN: if(sscanf(xml, "%u", &u) != 1) { goto malformed_err; } if(u > 1) { goto range_err; } g_value_set_boolean(value, u); break; case G_TYPE_INT: if(sscanf(xml, "%d", &i) != 1) { goto malformed_err; } g_value_set_int(value, i); break; case G_TYPE_UINT: if(sscanf(xml, "%u", &u) != 1) { goto malformed_err; } g_value_set_uint(value, u); break; case G_TYPE_LONG: if(sscanf(xml, "%ld", &li) != 1) { goto malformed_err; } g_value_set_long(value, li); break; case G_TYPE_ULONG: if(sscanf(xml, "%lu", &lu) != 1) { goto malformed_err; } g_value_set_ulong(value, lu); break; case G_TYPE_INT64: i64 = g_ascii_strtoll(xml, &endptr, 10); if(endptr == xml) { goto malformed_err; } g_value_set_int64(value, i64); break; case G_TYPE_UINT64: u64 = g_ascii_strtoull(xml, &endptr, 10); if(endptr == xml) { goto malformed_err; } g_value_set_uint64(value, u64); break; case G_TYPE_ENUM: if(sscanf(xml, "%d", &i) != 1) { goto malformed_err; } g_value_set_enum(value, i); break; case G_TYPE_FLAGS: if(sscanf(xml, "%u", &u) != 1) { goto malformed_err; } g_value_set_flags(value, u); break; case G_TYPE_FLOAT: if(sscanf(xml, "%f", &f) != 1) { goto malformed_err; } g_value_set_float(value, f); break; case G_TYPE_DOUBLE: if(sscanf(xml, "%lf", &d) != 1) { goto malformed_err; } g_value_set_double(value, d); break; case G_TYPE_STRING: g_value_set_string(value, xml); break; default: if(valtype == G_TYPE_GTYPE) { g_value_set_gtype(value, g_type_from_name(xml)); return (TRUE); } else { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNHANDLED_CONVERSION, "Unhandled XML to GValue conversion for type '%s'", g_type_name(valtype)); return (FALSE); } } return (TRUE); malformed_err: g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID, "Invalid XML GValue '%s' for type '%s'", xml, g_type_name(valtype)); return (FALSE); range_err: g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_INVALID, "Out of range XML GValue '%s' for type '%s'", xml, g_type_name(valtype)); return (FALSE); } libinstpatch-1.1.6/libinstpatch/IpatchXmlObject.h000066400000000000000000000130731400263525300220640ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_XML_OBJECT_H__ #define __IPATCH_XML_OBJECT_H__ #include #include /** * IpatchXmlEncodeFunc: * @node: XML node to encode XML to * @object: Object being encoded (object and property encoders) * @pspec: Spec of property being encoded (property encoders only) * @value: Value to encode (property and GValue encoders only) * @err: Location to store error value (or %NULL if ignoring) * * Function type for encoding objects, properties or GValue types to XML trees. * Forms the basis of serializing GObject and GValues to XML. The caller * handles creating an XML node element to contain the given object, property or * value XML encoding. * * Returns: Should return %TRUE on success and %FALSE on error (in which * case @err should be set). */ typedef gboolean(*IpatchXmlEncodeFunc)(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err); /** * IpatchXmlDecodeFunc: * @node: XML node to be decoded * @object: Object being decoded to (object and property decoders, %NULL otherwise) * @pspec: Spec of property being decoded (property decoders only, %NULL otherwise) * @value: Value to decode to (property and GValue decoders only, %NULL otherwise) * @err: Location to store error value (or %NULL if ignoring) * * Function type for decoding objects, properties or GValue types from XML trees to * their original instance values in memory. For object decoders, only @object * will be set and the decoded XML content should be assigned to the object; * for property decoders @object, @pspec and @value will be set, @value is * initialized to the property value type and the decoded value should be * assigned to it; for GValue decoders, only the @value will be initialized * which the decoded value should be assigned to. * * Returns: Should return TRUE on success, FALSE otherwise (in which case @err * should be set) */ typedef gboolean(*IpatchXmlDecodeFunc)(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err); void ipatch_xml_register_handler(GType type, const char *prop_name, IpatchXmlEncodeFunc encode_func, IpatchXmlDecodeFunc decode_func); void ipatch_xml_register_handler_full(GType type, const char *prop_name, IpatchXmlEncodeFunc encode_func, IpatchXmlDecodeFunc decode_func, GDestroyNotify notify_func, gpointer user_data); gboolean ipatch_xml_lookup_handler(GType type, GParamSpec *pspec, IpatchXmlEncodeFunc *encode_func, IpatchXmlDecodeFunc *decode_func); gboolean ipatch_xml_lookup_handler_by_prop_name(GType type, const char *prop_name, IpatchXmlEncodeFunc *encode_func, IpatchXmlDecodeFunc *decode_func); gboolean ipatch_xml_encode_object(GNode *node, GObject *object, gboolean create_element, GError **err); gboolean ipatch_xml_encode_property(GNode *node, GObject *object, GParamSpec *pspec, gboolean create_element, GError **err); gboolean ipatch_xml_encode_property_by_name(GNode *node, GObject *object, const char *propname, gboolean create_element, GError **err); gboolean ipatch_xml_encode_value(GNode *node, GValue *value, GError **err); gboolean ipatch_xml_decode_object(GNode *node, GObject *object, GError **err); gboolean ipatch_xml_decode_property(GNode *node, GObject *object, GParamSpec *pspec, GError **err); gboolean ipatch_xml_decode_property_by_name(GNode *node, GObject *object, const char *propname, GError **err); gboolean ipatch_xml_decode_value(GNode *node, GValue *value, GError **err); gboolean ipatch_xml_default_encode_object_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err); gboolean ipatch_xml_default_encode_property_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err); gboolean ipatch_xml_default_encode_value_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err); gboolean ipatch_xml_default_decode_object_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err); gboolean ipatch_xml_default_decode_property_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err); gboolean ipatch_xml_default_decode_value_func(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err); #endif libinstpatch-1.1.6/libinstpatch/builtin_enums.c000066400000000000000000001675631400263525300217320ustar00rootroot00000000000000 /* This file is generated by glib-mkenums, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */ #include "libinstpatch.h" #include "ipatch_priv.h" /* enumerations from "IpatchBase.h" */ static const GFlagsValue _ipatch_base_flags_values[] = { { IPATCH_BASE_CHANGED, "IPATCH_BASE_CHANGED", "changed" }, { IPATCH_BASE_SAVED, "IPATCH_BASE_SAVED", "saved" }, { 0, NULL, NULL } }; GType ipatch_base_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchBaseFlags", _ipatch_base_flags_values); return type; } /* enumerations from "IpatchConverter.h" */ static const GEnumValue _ipatch_converter_log_type_values[] = { { IPATCH_CONVERTER_LOG_RATING, "IPATCH_CONVERTER_LOG_RATING", "rating" }, { IPATCH_CONVERTER_LOG_INFO, "IPATCH_CONVERTER_LOG_INFO", "info" }, { IPATCH_CONVERTER_LOG_WARN, "IPATCH_CONVERTER_LOG_WARN", "warn" }, { IPATCH_CONVERTER_LOG_CRITICAL, "IPATCH_CONVERTER_LOG_CRITICAL", "critical" }, { IPATCH_CONVERTER_LOG_FATAL, "IPATCH_CONVERTER_LOG_FATAL", "fatal" }, { 0, NULL, NULL } }; GType ipatch_converter_log_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchConverterLogType", _ipatch_converter_log_type_values); return type; } static const GEnumValue _ipatch_converter_count_values[] = { { IPATCH_CONVERTER_COUNT_ONE_OR_MORE, "IPATCH_CONVERTER_COUNT_ONE_OR_MORE", "one-or-more" }, { IPATCH_CONVERTER_COUNT_ZERO_OR_MORE, "IPATCH_CONVERTER_COUNT_ZERO_OR_MORE", "zero-or-more" }, { 0, NULL, NULL } }; GType ipatch_converter_count_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchConverterCount", _ipatch_converter_count_values); return type; } static const GFlagsValue _ipatch_converter_flags_values[] = { { IPATCH_CONVERTER_SRC_DERIVED, "IPATCH_CONVERTER_SRC_DERIVED", "src-derived" }, { IPATCH_CONVERTER_DEST_DERIVED, "IPATCH_CONVERTER_DEST_DERIVED", "dest-derived" }, { 0, NULL, NULL } }; GType ipatch_converter_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchConverterFlags", _ipatch_converter_flags_values); return type; } static const GEnumValue _ipatch_converter_priority_values[] = { { IPATCH_CONVERTER_PRIORITY_LOWEST, "IPATCH_CONVERTER_PRIORITY_LOWEST", "lowest" }, { IPATCH_CONVERTER_PRIORITY_LOW, "IPATCH_CONVERTER_PRIORITY_LOW", "low" }, { IPATCH_CONVERTER_PRIORITY_DEFAULT, "IPATCH_CONVERTER_PRIORITY_DEFAULT", "default" }, { IPATCH_CONVERTER_PRIORITY_HIGH, "IPATCH_CONVERTER_PRIORITY_HIGH", "high" }, { IPATCH_CONVERTER_PRIORITY_HIGHEST, "IPATCH_CONVERTER_PRIORITY_HIGHEST", "highest" }, { 0, NULL, NULL } }; GType ipatch_converter_priority_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchConverterPriority", _ipatch_converter_priority_values); return type; } /* enumerations from "IpatchDLS2.h" */ static const GFlagsValue _ipatch_dls2_flags_values[] = { { IPATCH_DLS2_VERSION_SET, "IPATCH_DLS2_VERSION_SET", "set" }, { 0, NULL, NULL } }; GType ipatch_dls2_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchDLS2Flags", _ipatch_dls2_flags_values); return type; } /* enumerations from "IpatchDLS2Conn.h" */ static const GEnumValue _ipatch_dls2_conn_src_type_values[] = { { IPATCH_DLS2_CONN_SRC_NONE, "IPATCH_DLS2_CONN_SRC_NONE", "none" }, { IPATCH_DLS2_CONN_SRC_LFO, "IPATCH_DLS2_CONN_SRC_LFO", "lfo" }, { IPATCH_DLS2_CONN_SRC_VELOCITY, "IPATCH_DLS2_CONN_SRC_VELOCITY", "velocity" }, { IPATCH_DLS2_CONN_SRC_NOTE, "IPATCH_DLS2_CONN_SRC_NOTE", "note" }, { IPATCH_DLS2_CONN_SRC_EG1, "IPATCH_DLS2_CONN_SRC_EG1", "eg1" }, { IPATCH_DLS2_CONN_SRC_EG2, "IPATCH_DLS2_CONN_SRC_EG2", "eg2" }, { IPATCH_DLS2_CONN_SRC_PITCH_WHEEL, "IPATCH_DLS2_CONN_SRC_PITCH_WHEEL", "pitch-wheel" }, { IPATCH_DLS2_CONN_SRC_POLY_PRESSURE, "IPATCH_DLS2_CONN_SRC_POLY_PRESSURE", "poly-pressure" }, { IPATCH_DLS2_CONN_SRC_CHANNEL_PRESSURE, "IPATCH_DLS2_CONN_SRC_CHANNEL_PRESSURE", "channel-pressure" }, { IPATCH_DLS2_CONN_SRC_VIBRATO, "IPATCH_DLS2_CONN_SRC_VIBRATO", "vibrato" }, { IPATCH_DLS2_CONN_SRC_CC1, "IPATCH_DLS2_CONN_SRC_CC1", "cc1" }, { IPATCH_DLS2_CONN_SRC_CC7, "IPATCH_DLS2_CONN_SRC_CC7", "cc7" }, { IPATCH_DLS2_CONN_SRC_CC10, "IPATCH_DLS2_CONN_SRC_CC10", "cc10" }, { IPATCH_DLS2_CONN_SRC_CC11, "IPATCH_DLS2_CONN_SRC_CC11", "cc11" }, { IPATCH_DLS2_CONN_SRC_CC91, "IPATCH_DLS2_CONN_SRC_CC91", "cc91" }, { IPATCH_DLS2_CONN_SRC_CC93, "IPATCH_DLS2_CONN_SRC_CC93", "cc93" }, { IPATCH_DLS2_CONN_SRC_RPN0, "IPATCH_DLS2_CONN_SRC_RPN0", "rpn0" }, { IPATCH_DLS2_CONN_SRC_RPN1, "IPATCH_DLS2_CONN_SRC_RPN1", "rpn1" }, { IPATCH_DLS2_CONN_SRC_RPN2, "IPATCH_DLS2_CONN_SRC_RPN2", "rpn2" }, { 0, NULL, NULL } }; GType ipatch_dls2_conn_src_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchDLS2ConnSrcType", _ipatch_dls2_conn_src_type_values); return type; } static const GEnumValue _ipatch_dls2_conn_dest_type_values[] = { { IPATCH_DLS2_CONN_DEST_NONE, "IPATCH_DLS2_CONN_DEST_NONE", "none" }, { IPATCH_DLS2_CONN_DEST_GAIN, "IPATCH_DLS2_CONN_DEST_GAIN", "gain" }, { IPATCH_DLS2_CONN_DEST_RESERVED, "IPATCH_DLS2_CONN_DEST_RESERVED", "reserved" }, { IPATCH_DLS2_CONN_DEST_PITCH, "IPATCH_DLS2_CONN_DEST_PITCH", "pitch" }, { IPATCH_DLS2_CONN_DEST_PAN, "IPATCH_DLS2_CONN_DEST_PAN", "pan" }, { IPATCH_DLS2_CONN_DEST_NOTE, "IPATCH_DLS2_CONN_DEST_NOTE", "note" }, { IPATCH_DLS2_CONN_DEST_LEFT, "IPATCH_DLS2_CONN_DEST_LEFT", "left" }, { IPATCH_DLS2_CONN_DEST_RIGHT, "IPATCH_DLS2_CONN_DEST_RIGHT", "right" }, { IPATCH_DLS2_CONN_DEST_CENTER, "IPATCH_DLS2_CONN_DEST_CENTER", "center" }, { IPATCH_DLS2_CONN_DEST_LFE_CHANNEL, "IPATCH_DLS2_CONN_DEST_LFE_CHANNEL", "lfe-channel" }, { IPATCH_DLS2_CONN_DEST_LEFT_REAR, "IPATCH_DLS2_CONN_DEST_LEFT_REAR", "left-rear" }, { IPATCH_DLS2_CONN_DEST_RIGHT_REAR, "IPATCH_DLS2_CONN_DEST_RIGHT_REAR", "right-rear" }, { IPATCH_DLS2_CONN_DEST_CHORUS, "IPATCH_DLS2_CONN_DEST_CHORUS", "chorus" }, { IPATCH_DLS2_CONN_DEST_REVERB, "IPATCH_DLS2_CONN_DEST_REVERB", "reverb" }, { IPATCH_DLS2_CONN_DEST_LFO_FREQ, "IPATCH_DLS2_CONN_DEST_LFO_FREQ", "lfo-freq" }, { IPATCH_DLS2_CONN_DEST_LFO_DELAY, "IPATCH_DLS2_CONN_DEST_LFO_DELAY", "lfo-delay" }, { IPATCH_DLS2_CONN_DEST_VIB_FREQ, "IPATCH_DLS2_CONN_DEST_VIB_FREQ", "vib-freq" }, { IPATCH_DLS2_CONN_DEST_VIB_DELAY, "IPATCH_DLS2_CONN_DEST_VIB_DELAY", "vib-delay" }, { IPATCH_DLS2_CONN_DEST_EG1_ATTACK, "IPATCH_DLS2_CONN_DEST_EG1_ATTACK", "eg1-attack" }, { IPATCH_DLS2_CONN_DEST_EG1_DECAY, "IPATCH_DLS2_CONN_DEST_EG1_DECAY", "eg1-decay" }, { IPATCH_DLS2_CONN_DEST_EG1_RESERVED, "IPATCH_DLS2_CONN_DEST_EG1_RESERVED", "eg1-reserved" }, { IPATCH_DLS2_CONN_DEST_EG1_RELEASE, "IPATCH_DLS2_CONN_DEST_EG1_RELEASE", "eg1-release" }, { IPATCH_DLS2_CONN_DEST_EG1_SUSTAIN, "IPATCH_DLS2_CONN_DEST_EG1_SUSTAIN", "eg1-sustain" }, { IPATCH_DLS2_CONN_DEST_EG1_DELAY, "IPATCH_DLS2_CONN_DEST_EG1_DELAY", "eg1-delay" }, { IPATCH_DLS2_CONN_DEST_EG1_HOLD, "IPATCH_DLS2_CONN_DEST_EG1_HOLD", "eg1-hold" }, { IPATCH_DLS2_CONN_DEST_EG1_SHUTDOWN, "IPATCH_DLS2_CONN_DEST_EG1_SHUTDOWN", "eg1-shutdown" }, { IPATCH_DLS2_CONN_DEST_EG2_ATTACK, "IPATCH_DLS2_CONN_DEST_EG2_ATTACK", "eg2-attack" }, { IPATCH_DLS2_CONN_DEST_EG2_DECAY, "IPATCH_DLS2_CONN_DEST_EG2_DECAY", "eg2-decay" }, { IPATCH_DLS2_CONN_DEST_EG2_RESERVED, "IPATCH_DLS2_CONN_DEST_EG2_RESERVED", "eg2-reserved" }, { IPATCH_DLS2_CONN_DEST_EG2_RELEASE, "IPATCH_DLS2_CONN_DEST_EG2_RELEASE", "eg2-release" }, { IPATCH_DLS2_CONN_DEST_EG2_SUSTAIN, "IPATCH_DLS2_CONN_DEST_EG2_SUSTAIN", "eg2-sustain" }, { IPATCH_DLS2_CONN_DEST_EG2_DELAY, "IPATCH_DLS2_CONN_DEST_EG2_DELAY", "eg2-delay" }, { IPATCH_DLS2_CONN_DEST_EG2_HOLD, "IPATCH_DLS2_CONN_DEST_EG2_HOLD", "eg2-hold" }, { IPATCH_DLS2_CONN_DEST_FILTER_CUTOFF, "IPATCH_DLS2_CONN_DEST_FILTER_CUTOFF", "filter-cutoff" }, { IPATCH_DLS2_CONN_DEST_FILTER_Q, "IPATCH_DLS2_CONN_DEST_FILTER_Q", "filter-q" }, { 0, NULL, NULL } }; GType ipatch_dls2_conn_dest_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchDLS2ConnDestType", _ipatch_dls2_conn_dest_type_values); return type; } static const GEnumValue _ipatch_dls2_conn_transform_type_values[] = { { IPATCH_DLS2_CONN_TRANS_LINEAR, "IPATCH_DLS2_CONN_TRANS_LINEAR", "linear" }, { IPATCH_DLS2_CONN_TRANS_CONCAVE, "IPATCH_DLS2_CONN_TRANS_CONCAVE", "concave" }, { IPATCH_DLS2_CONN_TRANS_CONVEX, "IPATCH_DLS2_CONN_TRANS_CONVEX", "convex" }, { IPATCH_DLS2_CONN_TRANS_SWITCH, "IPATCH_DLS2_CONN_TRANS_SWITCH", "switch" }, { 0, NULL, NULL } }; GType ipatch_dls2_conn_transform_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchDLS2ConnTransformType", _ipatch_dls2_conn_transform_type_values); return type; } static const GEnumValue _ipatch_dls2_conn_polarity_type_values[] = { { IPATCH_DLS2_CONN_POLARITY_UNI, "IPATCH_DLS2_CONN_POLARITY_UNI", "uni" }, { IPATCH_DLS2_CONN_POLARITY_BI, "IPATCH_DLS2_CONN_POLARITY_BI", "bi" }, { 0, NULL, NULL } }; GType ipatch_dls2_conn_polarity_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchDLS2ConnPolarityType", _ipatch_dls2_conn_polarity_type_values); return type; } static const GEnumValue _ipatch_dls2_conn_transform_masks_values[] = { { IPATCH_DLS2_CONN_MASK_OUTPUT_TRANS, "IPATCH_DLS2_CONN_MASK_OUTPUT_TRANS", "output-trans" }, { IPATCH_DLS2_CONN_MASK_CTRLSRC_TRANS, "IPATCH_DLS2_CONN_MASK_CTRLSRC_TRANS", "ctrlsrc-trans" }, { IPATCH_DLS2_CONN_MASK_CTRLSRC_POLARITY, "IPATCH_DLS2_CONN_MASK_CTRLSRC_POLARITY", "ctrlsrc-polarity" }, { IPATCH_DLS2_CONN_MASK_CTRLSRC_INVERT, "IPATCH_DLS2_CONN_MASK_CTRLSRC_INVERT", "ctrlsrc-invert" }, { IPATCH_DLS2_CONN_MASK_SRC_TRANS, "IPATCH_DLS2_CONN_MASK_SRC_TRANS", "src-trans" }, { IPATCH_DLS2_CONN_MASK_SRC_POLARITY, "IPATCH_DLS2_CONN_MASK_SRC_POLARITY", "src-polarity" }, { IPATCH_DLS2_CONN_MASK_SRC_INVERT, "IPATCH_DLS2_CONN_MASK_SRC_INVERT", "src-invert" }, { 0, NULL, NULL } }; GType ipatch_dls2_conn_transform_masks_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchDLS2ConnTransformMasks", _ipatch_dls2_conn_transform_masks_values); return type; } static const GEnumValue _ipatch_dls2_conn_transform_shifts_values[] = { { IPATCH_DLS2_CONN_SHIFT_OUTPUT_TRANS, "IPATCH_DLS2_CONN_SHIFT_OUTPUT_TRANS", "output-trans" }, { IPATCH_DLS2_CONN_SHIFT_CTRLSRC_TRANS, "IPATCH_DLS2_CONN_SHIFT_CTRLSRC_TRANS", "ctrlsrc-trans" }, { IPATCH_DLS2_CONN_SHIFT_CTRLSRC_POLARITY, "IPATCH_DLS2_CONN_SHIFT_CTRLSRC_POLARITY", "ctrlsrc-polarity" }, { IPATCH_DLS2_CONN_SHIFT_CTRLSRC_INVERT, "IPATCH_DLS2_CONN_SHIFT_CTRLSRC_INVERT", "ctrlsrc-invert" }, { IPATCH_DLS2_CONN_SHIFT_SRC_TRANS, "IPATCH_DLS2_CONN_SHIFT_SRC_TRANS", "src-trans" }, { IPATCH_DLS2_CONN_SHIFT_SRC_POLARITY, "IPATCH_DLS2_CONN_SHIFT_SRC_POLARITY", "src-polarity" }, { IPATCH_DLS2_CONN_SHIFT_SRC_INVERT, "IPATCH_DLS2_CONN_SHIFT_SRC_INVERT", "src-invert" }, { 0, NULL, NULL } }; GType ipatch_dls2_conn_transform_shifts_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchDLS2ConnTransformShifts", _ipatch_dls2_conn_transform_shifts_values); return type; } /* enumerations from "IpatchDLS2Info.h" */ static const GEnumValue _ipatch_dls2_info_type_values[] = { { IPATCH_DLS2_ARCHIVE_LOCATION, "IPATCH_DLS2_ARCHIVE_LOCATION", "archive-location" }, { IPATCH_DLS2_ARTIST, "IPATCH_DLS2_ARTIST", "artist" }, { IPATCH_DLS2_COMMISSIONED, "IPATCH_DLS2_COMMISSIONED", "commissioned" }, { IPATCH_DLS2_COMMENT, "IPATCH_DLS2_COMMENT", "comment" }, { IPATCH_DLS2_COPYRIGHT, "IPATCH_DLS2_COPYRIGHT", "copyright" }, { IPATCH_DLS2_DATE, "IPATCH_DLS2_DATE", "date" }, { IPATCH_DLS2_ENGINEER, "IPATCH_DLS2_ENGINEER", "engineer" }, { IPATCH_DLS2_GENRE, "IPATCH_DLS2_GENRE", "genre" }, { IPATCH_DLS2_KEYWORDS, "IPATCH_DLS2_KEYWORDS", "keywords" }, { IPATCH_DLS2_MEDIUM, "IPATCH_DLS2_MEDIUM", "medium" }, { IPATCH_DLS2_NAME, "IPATCH_DLS2_NAME", "name" }, { IPATCH_DLS2_PRODUCT, "IPATCH_DLS2_PRODUCT", "product" }, { IPATCH_DLS2_SUBJECT, "IPATCH_DLS2_SUBJECT", "subject" }, { IPATCH_DLS2_SOFTWARE, "IPATCH_DLS2_SOFTWARE", "software" }, { IPATCH_DLS2_SOURCE, "IPATCH_DLS2_SOURCE", "source" }, { IPATCH_DLS2_SOURCE_FORM, "IPATCH_DLS2_SOURCE_FORM", "source-form" }, { IPATCH_DLS2_TECHNICIAN, "IPATCH_DLS2_TECHNICIAN", "technician" }, { 0, NULL, NULL } }; GType ipatch_dls2_info_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchDLS2InfoType", _ipatch_dls2_info_type_values); return type; } /* enumerations from "IpatchDLS2Inst.h" */ static const GFlagsValue _ipatch_dls2_inst_flags_values[] = { { IPATCH_DLS2_INST_PERCUSSION, "IPATCH_DLS2_INST_PERCUSSION", "percussion" }, { 0, NULL, NULL } }; GType ipatch_dls2_inst_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchDLS2InstFlags", _ipatch_dls2_inst_flags_values); return type; } /* enumerations from "IpatchDLS2Region.h" */ static const GEnumValue _ipatch_dls2_param_values[] = { { IPATCH_DLS2_PARAM_MOD_LFO_FREQ, "IPATCH_DLS2_PARAM_MOD_LFO_FREQ", "mod-lfo-freq" }, { IPATCH_DLS2_PARAM_MOD_LFO_DELAY, "IPATCH_DLS2_PARAM_MOD_LFO_DELAY", "mod-lfo-delay" }, { IPATCH_DLS2_PARAM_VIB_LFO_FREQ, "IPATCH_DLS2_PARAM_VIB_LFO_FREQ", "vib-lfo-freq" }, { IPATCH_DLS2_PARAM_VIB_LFO_DELAY, "IPATCH_DLS2_PARAM_VIB_LFO_DELAY", "vib-lfo-delay" }, { IPATCH_DLS2_PARAM_VOL_EG_DELAY, "IPATCH_DLS2_PARAM_VOL_EG_DELAY", "vol-eg-delay" }, { IPATCH_DLS2_PARAM_VOL_EG_ATTACK, "IPATCH_DLS2_PARAM_VOL_EG_ATTACK", "vol-eg-attack" }, { IPATCH_DLS2_PARAM_VOL_EG_HOLD, "IPATCH_DLS2_PARAM_VOL_EG_HOLD", "vol-eg-hold" }, { IPATCH_DLS2_PARAM_VOL_EG_DECAY, "IPATCH_DLS2_PARAM_VOL_EG_DECAY", "vol-eg-decay" }, { IPATCH_DLS2_PARAM_VOL_EG_SUSTAIN, "IPATCH_DLS2_PARAM_VOL_EG_SUSTAIN", "vol-eg-sustain" }, { IPATCH_DLS2_PARAM_VOL_EG_RELEASE, "IPATCH_DLS2_PARAM_VOL_EG_RELEASE", "vol-eg-release" }, { IPATCH_DLS2_PARAM_VOL_EG_SHUTDOWN, "IPATCH_DLS2_PARAM_VOL_EG_SHUTDOWN", "vol-eg-shutdown" }, { IPATCH_DLS2_PARAM_VOL_EG_VELOCITY_TO_ATTACK, "IPATCH_DLS2_PARAM_VOL_EG_VELOCITY_TO_ATTACK", "vol-eg-velocity-to-attack" }, { IPATCH_DLS2_PARAM_VOL_EG_NOTE_TO_DECAY, "IPATCH_DLS2_PARAM_VOL_EG_NOTE_TO_DECAY", "vol-eg-note-to-decay" }, { IPATCH_DLS2_PARAM_VOL_EG_NOTE_TO_HOLD, "IPATCH_DLS2_PARAM_VOL_EG_NOTE_TO_HOLD", "vol-eg-note-to-hold" }, { IPATCH_DLS2_PARAM_MOD_EG_DELAY, "IPATCH_DLS2_PARAM_MOD_EG_DELAY", "mod-eg-delay" }, { IPATCH_DLS2_PARAM_MOD_EG_ATTACK, "IPATCH_DLS2_PARAM_MOD_EG_ATTACK", "mod-eg-attack" }, { IPATCH_DLS2_PARAM_MOD_EG_HOLD, "IPATCH_DLS2_PARAM_MOD_EG_HOLD", "mod-eg-hold" }, { IPATCH_DLS2_PARAM_MOD_EG_DECAY, "IPATCH_DLS2_PARAM_MOD_EG_DECAY", "mod-eg-decay" }, { IPATCH_DLS2_PARAM_MOD_EG_SUSTAIN, "IPATCH_DLS2_PARAM_MOD_EG_SUSTAIN", "mod-eg-sustain" }, { IPATCH_DLS2_PARAM_MOD_EG_RELEASE, "IPATCH_DLS2_PARAM_MOD_EG_RELEASE", "mod-eg-release" }, { IPATCH_DLS2_PARAM_MOD_EG_VELOCITY_TO_ATTACK, "IPATCH_DLS2_PARAM_MOD_EG_VELOCITY_TO_ATTACK", "mod-eg-velocity-to-attack" }, { IPATCH_DLS2_PARAM_MOD_EG_NOTE_TO_DECAY, "IPATCH_DLS2_PARAM_MOD_EG_NOTE_TO_DECAY", "mod-eg-note-to-decay" }, { IPATCH_DLS2_PARAM_MOD_EG_NOTE_TO_HOLD, "IPATCH_DLS2_PARAM_MOD_EG_NOTE_TO_HOLD", "mod-eg-note-to-hold" }, { IPATCH_DLS2_PARAM_SCALE_TUNE, "IPATCH_DLS2_PARAM_SCALE_TUNE", "scale-tune" }, { IPATCH_DLS2_PARAM_RPN2_TO_NOTE, "IPATCH_DLS2_PARAM_RPN2_TO_NOTE", "rpn2-to-note" }, { IPATCH_DLS2_PARAM_FILTER_CUTOFF, "IPATCH_DLS2_PARAM_FILTER_CUTOFF", "filter-cutoff" }, { IPATCH_DLS2_PARAM_FILTER_Q, "IPATCH_DLS2_PARAM_FILTER_Q", "filter-q" }, { IPATCH_DLS2_PARAM_MOD_LFO_TO_FILTER_CUTOFF, "IPATCH_DLS2_PARAM_MOD_LFO_TO_FILTER_CUTOFF", "mod-lfo-to-filter-cutoff" }, { IPATCH_DLS2_PARAM_MOD_LFO_CC1_TO_FILTER_CUTOFF, "IPATCH_DLS2_PARAM_MOD_LFO_CC1_TO_FILTER_CUTOFF", "mod-lfo-cc1-to-filter-cutoff" }, { IPATCH_DLS2_PARAM_MOD_LFO_CHANNEL_PRESS_TO_FILTER_CUTOFF, "IPATCH_DLS2_PARAM_MOD_LFO_CHANNEL_PRESS_TO_FILTER_CUTOFF", "mod-lfo-channel-press-to-filter-cutoff" }, { IPATCH_DLS2_PARAM_MOD_EG_TO_FILTER_CUTOFF, "IPATCH_DLS2_PARAM_MOD_EG_TO_FILTER_CUTOFF", "mod-eg-to-filter-cutoff" }, { IPATCH_DLS2_PARAM_VELOCITY_TO_FILTER_CUTOFF, "IPATCH_DLS2_PARAM_VELOCITY_TO_FILTER_CUTOFF", "velocity-to-filter-cutoff" }, { IPATCH_DLS2_PARAM_NOTE_TO_FILTER_CUTOFF, "IPATCH_DLS2_PARAM_NOTE_TO_FILTER_CUTOFF", "note-to-filter-cutoff" }, { IPATCH_DLS2_PARAM_MOD_LFO_TO_GAIN, "IPATCH_DLS2_PARAM_MOD_LFO_TO_GAIN", "mod-lfo-to-gain" }, { IPATCH_DLS2_PARAM_MOD_LFO_CC1_TO_GAIN, "IPATCH_DLS2_PARAM_MOD_LFO_CC1_TO_GAIN", "mod-lfo-cc1-to-gain" }, { IPATCH_DLS2_PARAM_MOD_LFO_CHANNEL_PRESS_TO_GAIN, "IPATCH_DLS2_PARAM_MOD_LFO_CHANNEL_PRESS_TO_GAIN", "mod-lfo-channel-press-to-gain" }, { IPATCH_DLS2_PARAM_VELOCITY_TO_GAIN, "IPATCH_DLS2_PARAM_VELOCITY_TO_GAIN", "velocity-to-gain" }, { IPATCH_DLS2_PARAM_CC7_TO_GAIN, "IPATCH_DLS2_PARAM_CC7_TO_GAIN", "cc7-to-gain" }, { IPATCH_DLS2_PARAM_CC11_TO_GAIN, "IPATCH_DLS2_PARAM_CC11_TO_GAIN", "cc11-to-gain" }, { IPATCH_DLS2_PARAM_TUNE, "IPATCH_DLS2_PARAM_TUNE", "tune" }, { IPATCH_DLS2_PARAM_PITCH_WHEEL_RPN0_TO_PITCH, "IPATCH_DLS2_PARAM_PITCH_WHEEL_RPN0_TO_PITCH", "pitch-wheel-rpn0-to-pitch" }, { IPATCH_DLS2_PARAM_NOTE_NUMBER_TO_PITCH, "IPATCH_DLS2_PARAM_NOTE_NUMBER_TO_PITCH", "note-number-to-pitch" }, { IPATCH_DLS2_PARAM_RPN1_TO_PITCH, "IPATCH_DLS2_PARAM_RPN1_TO_PITCH", "rpn1-to-pitch" }, { IPATCH_DLS2_PARAM_VIB_LFO_TO_PITCH, "IPATCH_DLS2_PARAM_VIB_LFO_TO_PITCH", "vib-lfo-to-pitch" }, { IPATCH_DLS2_PARAM_VIB_LFO_CC1_TO_PITCH, "IPATCH_DLS2_PARAM_VIB_LFO_CC1_TO_PITCH", "vib-lfo-cc1-to-pitch" }, { IPATCH_DLS2_PARAM_VIB_LFO_CHANNEL_PRESS_TO_PITCH, "IPATCH_DLS2_PARAM_VIB_LFO_CHANNEL_PRESS_TO_PITCH", "vib-lfo-channel-press-to-pitch" }, { IPATCH_DLS2_PARAM_MOD_LFO_TO_PITCH, "IPATCH_DLS2_PARAM_MOD_LFO_TO_PITCH", "mod-lfo-to-pitch" }, { IPATCH_DLS2_PARAM_MOD_LFO_CC1_TO_PITCH, "IPATCH_DLS2_PARAM_MOD_LFO_CC1_TO_PITCH", "mod-lfo-cc1-to-pitch" }, { IPATCH_DLS2_PARAM_MOD_LFO_CHANNEL_PRESS_TO_PITCH, "IPATCH_DLS2_PARAM_MOD_LFO_CHANNEL_PRESS_TO_PITCH", "mod-lfo-channel-press-to-pitch" }, { IPATCH_DLS2_PARAM_MOD_EG_TO_PITCH, "IPATCH_DLS2_PARAM_MOD_EG_TO_PITCH", "mod-eg-to-pitch" }, { IPATCH_DLS2_PARAM_PAN, "IPATCH_DLS2_PARAM_PAN", "pan" }, { IPATCH_DLS2_PARAM_CC10_TO_PAN, "IPATCH_DLS2_PARAM_CC10_TO_PAN", "cc10-to-pan" }, { IPATCH_DLS2_PARAM_CC91_TO_REVERB_SEND, "IPATCH_DLS2_PARAM_CC91_TO_REVERB_SEND", "cc91-to-reverb-send" }, { IPATCH_DLS2_PARAM_REVERB_SEND, "IPATCH_DLS2_PARAM_REVERB_SEND", "reverb-send" }, { IPATCH_DLS2_PARAM_CC93_TO_CHORUS_SEND, "IPATCH_DLS2_PARAM_CC93_TO_CHORUS_SEND", "cc93-to-chorus-send" }, { IPATCH_DLS2_PARAM_CHORUS_SEND, "IPATCH_DLS2_PARAM_CHORUS_SEND", "chorus-send" }, { IPATCH_DLS2_PARAM_COUNT, "IPATCH_DLS2_PARAM_COUNT", "count" }, { 0, NULL, NULL } }; GType ipatch_dls2_param_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchDLS2Param", _ipatch_dls2_param_values); return type; } static const GEnumValue _ipatch_dls2_region_channel_type_values[] = { { IPATCH_DLS2_REGION_CHANNEL_LEFT, "IPATCH_DLS2_REGION_CHANNEL_LEFT", "left" }, { IPATCH_DLS2_REGION_CHANNEL_RIGHT, "IPATCH_DLS2_REGION_CHANNEL_RIGHT", "right" }, { IPATCH_DLS2_REGION_CHANNEL_CENTER, "IPATCH_DLS2_REGION_CHANNEL_CENTER", "center" }, { IPATCH_DLS2_REGION_CHANNEL_LOW_FREQ, "IPATCH_DLS2_REGION_CHANNEL_LOW_FREQ", "low-freq" }, { IPATCH_DLS2_REGION_CHANNEL_SURROUND_LEFT, "IPATCH_DLS2_REGION_CHANNEL_SURROUND_LEFT", "surround-left" }, { IPATCH_DLS2_REGION_CHANNEL_SURROUND_RIGHT, "IPATCH_DLS2_REGION_CHANNEL_SURROUND_RIGHT", "surround-right" }, { IPATCH_DLS2_REGION_CHANNEL_LEFT_OF_CENTER, "IPATCH_DLS2_REGION_CHANNEL_LEFT_OF_CENTER", "left-of-center" }, { IPATCH_DLS2_REGION_CHANNEL_RIGHT_OF_CENTER, "IPATCH_DLS2_REGION_CHANNEL_RIGHT_OF_CENTER", "right-of-center" }, { IPATCH_DLS2_REGION_CHANNEL_SURROUND_CENTER, "IPATCH_DLS2_REGION_CHANNEL_SURROUND_CENTER", "surround-center" }, { IPATCH_DLS2_REGION_CHANNEL_SIDE_LEFT, "IPATCH_DLS2_REGION_CHANNEL_SIDE_LEFT", "side-left" }, { IPATCH_DLS2_REGION_CHANNEL_SIDE_RIGHT, "IPATCH_DLS2_REGION_CHANNEL_SIDE_RIGHT", "side-right" }, { IPATCH_DLS2_REGION_CHANNEL_TOP, "IPATCH_DLS2_REGION_CHANNEL_TOP", "top" }, { IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_LEFT, "IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_LEFT", "top-front-left" }, { IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_CENTER, "IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_CENTER", "top-front-center" }, { IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_RIGHT, "IPATCH_DLS2_REGION_CHANNEL_TOP_FRONT_RIGHT", "top-front-right" }, { IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_LEFT, "IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_LEFT", "top-rear-left" }, { IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_CENTER, "IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_CENTER", "top-rear-center" }, { IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_RIGHT, "IPATCH_DLS2_REGION_CHANNEL_TOP_REAR_RIGHT", "top-rear-right" }, { 0, NULL, NULL } }; GType ipatch_dls2_region_channel_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchDLS2RegionChannelType", _ipatch_dls2_region_channel_type_values); return type; } static const GFlagsValue _ipatch_dls2_region_flags_values[] = { { IPATCH_DLS2_REGION_SELF_NON_EXCLUSIVE, "IPATCH_DLS2_REGION_SELF_NON_EXCLUSIVE", "self-non-exclusive" }, { IPATCH_DLS2_REGION_PHASE_MASTER, "IPATCH_DLS2_REGION_PHASE_MASTER", "phase-master" }, { IPATCH_DLS2_REGION_MULTI_CHANNEL, "IPATCH_DLS2_REGION_MULTI_CHANNEL", "multi-channel" }, { IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE, "IPATCH_DLS2_REGION_SAMPLE_INFO_OVERRIDE", "sample-info-override" }, { 0, NULL, NULL } }; GType ipatch_dls2_region_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchDLS2RegionFlags", _ipatch_dls2_region_flags_values); return type; } /* enumerations from "IpatchDLS2Sample.h" */ static const GFlagsValue _ipatch_dls2_sample_flags_values[] = { { IPATCH_DLS2_SAMPLE_NO_TRUNCATION, "IPATCH_DLS2_SAMPLE_NO_TRUNCATION", "truncation" }, { IPATCH_DLS2_SAMPLE_NO_COMPRESSION, "IPATCH_DLS2_SAMPLE_NO_COMPRESSION", "compression" }, { 0, NULL, NULL } }; GType ipatch_dls2_sample_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchDLS2SampleFlags", _ipatch_dls2_sample_flags_values); return type; } /* enumerations from "IpatchDLSReader.h" */ static const GEnumValue _ipatch_dls_reader_error_values[] = { { IPATCH_DLS_READER_ERROR_GIG, "IPATCH_DLS_READER_ERROR_GIG", "gig" }, { 0, NULL, NULL } }; GType ipatch_dls_reader_error_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchDLSReaderError", _ipatch_dls_reader_error_values); return type; } /* enumerations from "IpatchFile.h" */ static const GFlagsValue _ipatch_file_flags_values[] = { { IPATCH_FILE_FLAG_SWAP, "IPATCH_FILE_FLAG_SWAP", "swap" }, { IPATCH_FILE_FLAG_BIG_ENDIAN, "IPATCH_FILE_FLAG_BIG_ENDIAN", "big-endian" }, { IPATCH_FILE_FLAG_FREE_IOFUNCS, "IPATCH_FILE_FLAG_FREE_IOFUNCS", "free-iofuncs" }, { 0, NULL, NULL } }; GType ipatch_file_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchFileFlags", _ipatch_file_flags_values); return type; } static const GEnumValue _ipatch_file_identify_order_values[] = { { IPATCH_FILE_IDENTIFY_ORDER_LAST, "IPATCH_FILE_IDENTIFY_ORDER_LAST", "last" }, { IPATCH_FILE_IDENTIFY_ORDER_DEFAULT, "IPATCH_FILE_IDENTIFY_ORDER_DEFAULT", "default" }, { IPATCH_FILE_IDENTIFY_ORDER_FIRST, "IPATCH_FILE_IDENTIFY_ORDER_FIRST", "first" }, { 0, NULL, NULL } }; GType ipatch_file_identify_order_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchFileIdentifyOrder", _ipatch_file_identify_order_values); return type; } /* enumerations from "IpatchGigDimension.h" */ static const GEnumValue _ipatch_gig_dimension_type_values[] = { { IPATCH_GIG_DIMENSION_NONE, "IPATCH_GIG_DIMENSION_NONE", "none" }, { IPATCH_GIG_DIMENSION_MOD_WHEEL, "IPATCH_GIG_DIMENSION_MOD_WHEEL", "mod-wheel" }, { IPATCH_GIG_DIMENSION_BREATH, "IPATCH_GIG_DIMENSION_BREATH", "breath" }, { IPATCH_GIG_DIMENSION_FOOT, "IPATCH_GIG_DIMENSION_FOOT", "foot" }, { IPATCH_GIG_DIMENSION_PORTAMENTO_TIME, "IPATCH_GIG_DIMENSION_PORTAMENTO_TIME", "portamento-time" }, { IPATCH_GIG_DIMENSION_EFFECT_1, "IPATCH_GIG_DIMENSION_EFFECT_1", "effect-1" }, { IPATCH_GIG_DIMENSION_EFFECT_2, "IPATCH_GIG_DIMENSION_EFFECT_2", "effect-2" }, { IPATCH_GIG_DIMENSION_GEN_PURPOSE_1, "IPATCH_GIG_DIMENSION_GEN_PURPOSE_1", "gen-purpose-1" }, { IPATCH_GIG_DIMENSION_GEN_PURPOSE_2, "IPATCH_GIG_DIMENSION_GEN_PURPOSE_2", "gen-purpose-2" }, { IPATCH_GIG_DIMENSION_GEN_PURPOSE_3, "IPATCH_GIG_DIMENSION_GEN_PURPOSE_3", "gen-purpose-3" }, { IPATCH_GIG_DIMENSION_GEN_PURPOSE_4, "IPATCH_GIG_DIMENSION_GEN_PURPOSE_4", "gen-purpose-4" }, { IPATCH_GIG_DIMENSION_SUSTAIN_PEDAL, "IPATCH_GIG_DIMENSION_SUSTAIN_PEDAL", "sustain-pedal" }, { IPATCH_GIG_DIMENSION_PORTAMENTO, "IPATCH_GIG_DIMENSION_PORTAMENTO", "portamento" }, { IPATCH_GIG_DIMENSION_SOSTENUTO, "IPATCH_GIG_DIMENSION_SOSTENUTO", "sostenuto" }, { IPATCH_GIG_DIMENSION_SOFT_PEDAL, "IPATCH_GIG_DIMENSION_SOFT_PEDAL", "soft-pedal" }, { IPATCH_GIG_DIMENSION_GEN_PURPOSE_5, "IPATCH_GIG_DIMENSION_GEN_PURPOSE_5", "gen-purpose-5" }, { IPATCH_GIG_DIMENSION_GEN_PURPOSE_6, "IPATCH_GIG_DIMENSION_GEN_PURPOSE_6", "gen-purpose-6" }, { IPATCH_GIG_DIMENSION_GEN_PURPOSE_7, "IPATCH_GIG_DIMENSION_GEN_PURPOSE_7", "gen-purpose-7" }, { IPATCH_GIG_DIMENSION_GEN_PURPOSE_8, "IPATCH_GIG_DIMENSION_GEN_PURPOSE_8", "gen-purpose-8" }, { IPATCH_GIG_DIMENSION_EFFECT_DEPTH_1, "IPATCH_GIG_DIMENSION_EFFECT_DEPTH_1", "effect-depth-1" }, { IPATCH_GIG_DIMENSION_EFFECT_DEPTH_2, "IPATCH_GIG_DIMENSION_EFFECT_DEPTH_2", "effect-depth-2" }, { IPATCH_GIG_DIMENSION_EFFECT_DEPTH_3, "IPATCH_GIG_DIMENSION_EFFECT_DEPTH_3", "effect-depth-3" }, { IPATCH_GIG_DIMENSION_EFFECT_DEPTH_4, "IPATCH_GIG_DIMENSION_EFFECT_DEPTH_4", "effect-depth-4" }, { IPATCH_GIG_DIMENSION_EFFECT_DEPTH_5, "IPATCH_GIG_DIMENSION_EFFECT_DEPTH_5", "effect-depth-5" }, { IPATCH_GIG_DIMENSION_CHANNEL, "IPATCH_GIG_DIMENSION_CHANNEL", "channel" }, { IPATCH_GIG_DIMENSION_LAYER, "IPATCH_GIG_DIMENSION_LAYER", "layer" }, { IPATCH_GIG_DIMENSION_VELOCITY, "IPATCH_GIG_DIMENSION_VELOCITY", "velocity" }, { IPATCH_GIG_DIMENSION_AFTER_TOUCH, "IPATCH_GIG_DIMENSION_AFTER_TOUCH", "after-touch" }, { IPATCH_GIG_DIMENSION_RELEASE_TRIG, "IPATCH_GIG_DIMENSION_RELEASE_TRIG", "release-trig" }, { IPATCH_GIG_DIMENSION_KEYBOARD, "IPATCH_GIG_DIMENSION_KEYBOARD", "keyboard" }, { IPATCH_GIG_DIMENSION_ROUND_ROBIN, "IPATCH_GIG_DIMENSION_ROUND_ROBIN", "round-robin" }, { IPATCH_GIG_DIMENSION_RANDOM, "IPATCH_GIG_DIMENSION_RANDOM", "random" }, { 0, NULL, NULL } }; GType ipatch_gig_dimension_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchGigDimensionType", _ipatch_gig_dimension_type_values); return type; } /* enumerations from "IpatchGigEffects.h" */ static const GEnumValue _ipatch_gig_filter_type_values[] = { { IPATCH_GIG_FILTER_LOWPASS, "IPATCH_GIG_FILTER_LOWPASS", "lowpass" }, { IPATCH_GIG_FILTER_BANDPASS, "IPATCH_GIG_FILTER_BANDPASS", "bandpass" }, { IPATCH_GIG_FILTER_HIGHPASS, "IPATCH_GIG_FILTER_HIGHPASS", "highpass" }, { IPATCH_GIG_FILTER_BANDREJECT, "IPATCH_GIG_FILTER_BANDREJECT", "bandreject" }, { 0, NULL, NULL } }; GType ipatch_gig_filter_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchGigFilterType", _ipatch_gig_filter_type_values); return type; } static const GEnumValue _ipatch_gig_control_type_values[] = { { IPATCH_GIG_CTRL_MOD_WHEEL, "IPATCH_GIG_CTRL_MOD_WHEEL", "mod-wheel" }, { IPATCH_GIG_CTRL_BREATH, "IPATCH_GIG_CTRL_BREATH", "breath" }, { IPATCH_GIG_CTRL_FOOT, "IPATCH_GIG_CTRL_FOOT", "foot" }, { IPATCH_GIG_CTRL_PORTAMENTO_TIME, "IPATCH_GIG_CTRL_PORTAMENTO_TIME", "portamento-time" }, { IPATCH_GIG_CTRL_EFFECT_1, "IPATCH_GIG_CTRL_EFFECT_1", "effect-1" }, { IPATCH_GIG_CTRL_EFFECT_2, "IPATCH_GIG_CTRL_EFFECT_2", "effect-2" }, { IPATCH_GIG_CTRL_GEN_PURPOSE_1, "IPATCH_GIG_CTRL_GEN_PURPOSE_1", "gen-purpose-1" }, { IPATCH_GIG_CTRL_GEN_PURPOSE_2, "IPATCH_GIG_CTRL_GEN_PURPOSE_2", "gen-purpose-2" }, { IPATCH_GIG_CTRL_GEN_PURPOSE_3, "IPATCH_GIG_CTRL_GEN_PURPOSE_3", "gen-purpose-3" }, { IPATCH_GIG_CTRL_GEN_PURPOSE_4, "IPATCH_GIG_CTRL_GEN_PURPOSE_4", "gen-purpose-4" }, { IPATCH_GIG_CTRL_SUSTAIN_PEDAL, "IPATCH_GIG_CTRL_SUSTAIN_PEDAL", "sustain-pedal" }, { IPATCH_GIG_CTRL_PORTAMENTO, "IPATCH_GIG_CTRL_PORTAMENTO", "portamento" }, { IPATCH_GIG_CTRL_SOSTENUTO, "IPATCH_GIG_CTRL_SOSTENUTO", "sostenuto" }, { IPATCH_GIG_CTRL_SOFT_PEDAL, "IPATCH_GIG_CTRL_SOFT_PEDAL", "soft-pedal" }, { IPATCH_GIG_CTRL_GEN_PURPOSE_5, "IPATCH_GIG_CTRL_GEN_PURPOSE_5", "gen-purpose-5" }, { IPATCH_GIG_CTRL_GEN_PURPOSE_6, "IPATCH_GIG_CTRL_GEN_PURPOSE_6", "gen-purpose-6" }, { IPATCH_GIG_CTRL_GEN_PURPOSE_7, "IPATCH_GIG_CTRL_GEN_PURPOSE_7", "gen-purpose-7" }, { IPATCH_GIG_CTRL_GEN_PURPOSE_8, "IPATCH_GIG_CTRL_GEN_PURPOSE_8", "gen-purpose-8" }, { IPATCH_GIG_CTRL_EFFECT_DEPTH_1, "IPATCH_GIG_CTRL_EFFECT_DEPTH_1", "effect-depth-1" }, { IPATCH_GIG_CTRL_EFFECT_DEPTH_2, "IPATCH_GIG_CTRL_EFFECT_DEPTH_2", "effect-depth-2" }, { IPATCH_GIG_CTRL_EFFECT_DEPTH_3, "IPATCH_GIG_CTRL_EFFECT_DEPTH_3", "effect-depth-3" }, { IPATCH_GIG_CTRL_EFFECT_DEPTH_4, "IPATCH_GIG_CTRL_EFFECT_DEPTH_4", "effect-depth-4" }, { IPATCH_GIG_CTRL_EFFECT_DEPTH_5, "IPATCH_GIG_CTRL_EFFECT_DEPTH_5", "effect-depth-5" }, { 0, NULL, NULL } }; GType ipatch_gig_control_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchGigControlType", _ipatch_gig_control_type_values); return type; } /* enumerations from "IpatchGigRegion.h" */ static const GFlagsValue _ipatch_gig_region_flags_values[] = { { IPATCH_GIG_REGION_SELF_NON_EXCLUSIVE, "IPATCH_GIG_REGION_SELF_NON_EXCLUSIVE", "self-non-exclusive" }, { IPATCH_GIG_REGION_PHASE_MASTER, "IPATCH_GIG_REGION_PHASE_MASTER", "phase-master" }, { IPATCH_GIG_REGION_MULTI_CHANNEL, "IPATCH_GIG_REGION_MULTI_CHANNEL", "multi-channel" }, { 0, NULL, NULL } }; GType ipatch_gig_region_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchGigRegionFlags", _ipatch_gig_region_flags_values); return type; } /* enumerations from "IpatchGigSubRegion.h" */ static const GFlagsValue _ipatch_gig_sub_region_flags_values[] = { { IPATCH_GIG_SUB_REGION_SAMPLE_INFO_OVERRIDE, "IPATCH_GIG_SUB_REGION_SAMPLE_INFO_OVERRIDE", "override" }, { 0, NULL, NULL } }; GType ipatch_gig_sub_region_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchGigSubRegionFlags", _ipatch_gig_sub_region_flags_values); return type; } /* enumerations from "IpatchItem.h" */ static const GFlagsValue _ipatch_item_flags_values[] = { { IPATCH_ITEM_HOOKS_ACTIVE, "IPATCH_ITEM_HOOKS_ACTIVE", "hooks-active" }, { IPATCH_ITEM_FREE_MUTEX, "IPATCH_ITEM_FREE_MUTEX", "free-mutex" }, { 0, NULL, NULL } }; GType ipatch_item_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchItemFlags", _ipatch_item_flags_values); return type; } /* enumerations from "IpatchPaste.h" */ static const GEnumValue _ipatch_paste_choice_values[] = { { IPATCH_PASTE_CHOICE_IGNORE, "IPATCH_PASTE_CHOICE_IGNORE", "ignore" }, { IPATCH_PASTE_CHOICE_REPLACE, "IPATCH_PASTE_CHOICE_REPLACE", "replace" }, { IPATCH_PASTE_CHOICE_KEEP, "IPATCH_PASTE_CHOICE_KEEP", "keep" }, { IPATCH_PASTE_CHOICE_CANCEL, "IPATCH_PASTE_CHOICE_CANCEL", "cancel" }, { 0, NULL, NULL } }; GType ipatch_paste_choice_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchPasteChoice", _ipatch_paste_choice_values); return type; } static const GEnumValue _ipatch_paste_priority_values[] = { { IPATCH_PASTE_PRIORITY_LOWEST, "IPATCH_PASTE_PRIORITY_LOWEST", "lowest" }, { IPATCH_PASTE_PRIORITY_LOW, "IPATCH_PASTE_PRIORITY_LOW", "low" }, { IPATCH_PASTE_PRIORITY_DEFAULT, "IPATCH_PASTE_PRIORITY_DEFAULT", "default" }, { IPATCH_PASTE_PRIORITY_HIGH, "IPATCH_PASTE_PRIORITY_HIGH", "high" }, { IPATCH_PASTE_PRIORITY_HIGHEST, "IPATCH_PASTE_PRIORITY_HIGHEST", "highest" }, { 0, NULL, NULL } }; GType ipatch_paste_priority_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchPastePriority", _ipatch_paste_priority_values); return type; } /* enumerations from "IpatchRiff.h" */ static const GEnumValue _ipatch_riff_status_values[] = { { IPATCH_RIFF_STATUS_FAIL, "IPATCH_RIFF_STATUS_FAIL", "fail" }, { IPATCH_RIFF_STATUS_BEGIN, "IPATCH_RIFF_STATUS_BEGIN", "begin" }, { IPATCH_RIFF_STATUS_FINISHED, "IPATCH_RIFF_STATUS_FINISHED", "finished" }, { IPATCH_RIFF_STATUS_NORMAL, "IPATCH_RIFF_STATUS_NORMAL", "normal" }, { IPATCH_RIFF_STATUS_CHUNK_END, "IPATCH_RIFF_STATUS_CHUNK_END", "chunk-end" }, { 0, NULL, NULL } }; GType ipatch_riff_status_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchRiffStatus", _ipatch_riff_status_values); return type; } static const GEnumValue _ipatch_riff_mode_values[] = { { IPATCH_RIFF_READ, "IPATCH_RIFF_READ", "read" }, { IPATCH_RIFF_WRITE, "IPATCH_RIFF_WRITE", "write" }, { 0, NULL, NULL } }; GType ipatch_riff_mode_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchRiffMode", _ipatch_riff_mode_values); return type; } static const GEnumValue _ipatch_riff_chunk_type_values[] = { { IPATCH_RIFF_CHUNK_RIFF, "IPATCH_RIFF_CHUNK_RIFF", "riff" }, { IPATCH_RIFF_CHUNK_LIST, "IPATCH_RIFF_CHUNK_LIST", "list" }, { IPATCH_RIFF_CHUNK_SUB, "IPATCH_RIFF_CHUNK_SUB", "sub" }, { 0, NULL, NULL } }; GType ipatch_riff_chunk_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchRiffChunkType", _ipatch_riff_chunk_type_values); return type; } static const GEnumValue _ipatch_riff_error_values[] = { { IPATCH_RIFF_ERROR_NOT_RIFF, "IPATCH_RIFF_ERROR_NOT_RIFF", "not-riff" }, { IPATCH_RIFF_ERROR_UNEXPECTED_ID, "IPATCH_RIFF_ERROR_UNEXPECTED_ID", "unexpected-id" }, { IPATCH_RIFF_ERROR_UNEXPECTED_CHUNK_END, "IPATCH_RIFF_ERROR_UNEXPECTED_CHUNK_END", "unexpected-chunk-end" }, { IPATCH_RIFF_ERROR_INVALID_ID, "IPATCH_RIFF_ERROR_INVALID_ID", "invalid-id" }, { IPATCH_RIFF_ERROR_ODD_SIZE, "IPATCH_RIFF_ERROR_ODD_SIZE", "odd-size" }, { IPATCH_RIFF_ERROR_SIZE_EXCEEDED, "IPATCH_RIFF_ERROR_SIZE_EXCEEDED", "size-exceeded" }, { IPATCH_RIFF_ERROR_SIZE_MISMATCH, "IPATCH_RIFF_ERROR_SIZE_MISMATCH", "size-mismatch" }, { IPATCH_RIFF_ERROR_INVALID_DATA, "IPATCH_RIFF_ERROR_INVALID_DATA", "invalid-data" }, { 0, NULL, NULL } }; GType ipatch_riff_error_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchRiffError", _ipatch_riff_error_values); return type; } /* enumerations from "IpatchSF2.h" */ static const GFlagsValue _ipatch_sf2_flags_values[] = { { IPATCH_SF2_SAMPLES_24BIT, "IPATCH_SF2_SAMPLES_24BIT", "24bit" }, { 0, NULL, NULL } }; GType ipatch_sf2_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchSF2Flags", _ipatch_sf2_flags_values); return type; } static const GEnumValue _ipatch_sf2_info_type_values[] = { { IPATCH_SF2_UNKNOWN, "IPATCH_SF2_UNKNOWN", "unknown" }, { IPATCH_SF2_VERSION, "IPATCH_SF2_VERSION", "version" }, { IPATCH_SF2_ENGINE, "IPATCH_SF2_ENGINE", "engine" }, { IPATCH_SF2_NAME, "IPATCH_SF2_NAME", "name" }, { IPATCH_SF2_ROM_NAME, "IPATCH_SF2_ROM_NAME", "rom-name" }, { IPATCH_SF2_ROM_VERSION, "IPATCH_SF2_ROM_VERSION", "rom-version" }, { IPATCH_SF2_DATE, "IPATCH_SF2_DATE", "date" }, { IPATCH_SF2_AUTHOR, "IPATCH_SF2_AUTHOR", "author" }, { IPATCH_SF2_PRODUCT, "IPATCH_SF2_PRODUCT", "product" }, { IPATCH_SF2_COPYRIGHT, "IPATCH_SF2_COPYRIGHT", "copyright" }, { IPATCH_SF2_COMMENT, "IPATCH_SF2_COMMENT", "comment" }, { IPATCH_SF2_SOFTWARE, "IPATCH_SF2_SOFTWARE", "software" }, { 0, NULL, NULL } }; GType ipatch_sf2_info_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSF2InfoType", _ipatch_sf2_info_type_values); return type; } /* enumerations from "IpatchSF2Gen.h" */ static const GEnumValue _ipatch_sf2_gen_props_type_values[] = { { IPATCH_SF2_GEN_PROPS_INST, "IPATCH_SF2_GEN_PROPS_INST", "inst" }, { IPATCH_SF2_GEN_PROPS_PRESET, "IPATCH_SF2_GEN_PROPS_PRESET", "preset" }, { IPATCH_SF2_GEN_PROPS_INST_GLOBAL, "IPATCH_SF2_GEN_PROPS_INST_GLOBAL", "inst-global" }, { IPATCH_SF2_GEN_PROPS_PRESET_GLOBAL, "IPATCH_SF2_GEN_PROPS_PRESET_GLOBAL", "preset-global" }, { 0, NULL, NULL } }; GType ipatch_sf2_gen_props_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSF2GenPropsType", _ipatch_sf2_gen_props_type_values); return type; } static const GEnumValue _ipatch_sf2_gen_type_values[] = { { IPATCH_SF2_GEN_SAMPLE_START, "IPATCH_SF2_GEN_SAMPLE_START", "sample-start" }, { IPATCH_SF2_GEN_SAMPLE_END, "IPATCH_SF2_GEN_SAMPLE_END", "sample-end" }, { IPATCH_SF2_GEN_SAMPLE_LOOP_START, "IPATCH_SF2_GEN_SAMPLE_LOOP_START", "sample-loop-start" }, { IPATCH_SF2_GEN_SAMPLE_LOOP_END, "IPATCH_SF2_GEN_SAMPLE_LOOP_END", "sample-loop-end" }, { IPATCH_SF2_GEN_SAMPLE_COARSE_START, "IPATCH_SF2_GEN_SAMPLE_COARSE_START", "sample-coarse-start" }, { IPATCH_SF2_GEN_MOD_LFO_TO_PITCH, "IPATCH_SF2_GEN_MOD_LFO_TO_PITCH", "mod-lfo-to-pitch" }, { IPATCH_SF2_GEN_VIB_LFO_TO_PITCH, "IPATCH_SF2_GEN_VIB_LFO_TO_PITCH", "vib-lfo-to-pitch" }, { IPATCH_SF2_GEN_MOD_ENV_TO_PITCH, "IPATCH_SF2_GEN_MOD_ENV_TO_PITCH", "mod-env-to-pitch" }, { IPATCH_SF2_GEN_FILTER_CUTOFF, "IPATCH_SF2_GEN_FILTER_CUTOFF", "filter-cutoff" }, { IPATCH_SF2_GEN_FILTER_Q, "IPATCH_SF2_GEN_FILTER_Q", "filter-q" }, { IPATCH_SF2_GEN_MOD_LFO_TO_FILTER_CUTOFF, "IPATCH_SF2_GEN_MOD_LFO_TO_FILTER_CUTOFF", "mod-lfo-to-filter-cutoff" }, { IPATCH_SF2_GEN_MOD_ENV_TO_FILTER_CUTOFF, "IPATCH_SF2_GEN_MOD_ENV_TO_FILTER_CUTOFF", "mod-env-to-filter-cutoff" }, { IPATCH_SF2_GEN_SAMPLE_COARSE_END, "IPATCH_SF2_GEN_SAMPLE_COARSE_END", "sample-coarse-end" }, { IPATCH_SF2_GEN_MOD_LFO_TO_VOLUME, "IPATCH_SF2_GEN_MOD_LFO_TO_VOLUME", "mod-lfo-to-volume" }, { IPATCH_SF2_GEN_UNUSED1, "IPATCH_SF2_GEN_UNUSED1", "unused1" }, { IPATCH_SF2_GEN_CHORUS, "IPATCH_SF2_GEN_CHORUS", "chorus" }, { IPATCH_SF2_GEN_REVERB, "IPATCH_SF2_GEN_REVERB", "reverb" }, { IPATCH_SF2_GEN_PAN, "IPATCH_SF2_GEN_PAN", "pan" }, { IPATCH_SF2_GEN_UNUSED2, "IPATCH_SF2_GEN_UNUSED2", "unused2" }, { IPATCH_SF2_GEN_UNUSED3, "IPATCH_SF2_GEN_UNUSED3", "unused3" }, { IPATCH_SF2_GEN_UNUSED4, "IPATCH_SF2_GEN_UNUSED4", "unused4" }, { IPATCH_SF2_GEN_MOD_LFO_DELAY, "IPATCH_SF2_GEN_MOD_LFO_DELAY", "mod-lfo-delay" }, { IPATCH_SF2_GEN_MOD_LFO_FREQ, "IPATCH_SF2_GEN_MOD_LFO_FREQ", "mod-lfo-freq" }, { IPATCH_SF2_GEN_VIB_LFO_DELAY, "IPATCH_SF2_GEN_VIB_LFO_DELAY", "vib-lfo-delay" }, { IPATCH_SF2_GEN_VIB_LFO_FREQ, "IPATCH_SF2_GEN_VIB_LFO_FREQ", "vib-lfo-freq" }, { IPATCH_SF2_GEN_MOD_ENV_DELAY, "IPATCH_SF2_GEN_MOD_ENV_DELAY", "mod-env-delay" }, { IPATCH_SF2_GEN_MOD_ENV_ATTACK, "IPATCH_SF2_GEN_MOD_ENV_ATTACK", "mod-env-attack" }, { IPATCH_SF2_GEN_MOD_ENV_HOLD, "IPATCH_SF2_GEN_MOD_ENV_HOLD", "mod-env-hold" }, { IPATCH_SF2_GEN_MOD_ENV_DECAY, "IPATCH_SF2_GEN_MOD_ENV_DECAY", "mod-env-decay" }, { IPATCH_SF2_GEN_MOD_ENV_SUSTAIN, "IPATCH_SF2_GEN_MOD_ENV_SUSTAIN", "mod-env-sustain" }, { IPATCH_SF2_GEN_MOD_ENV_RELEASE, "IPATCH_SF2_GEN_MOD_ENV_RELEASE", "mod-env-release" }, { IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_HOLD, "IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_HOLD", "note-to-mod-env-hold" }, { IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_DECAY, "IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_DECAY", "note-to-mod-env-decay" }, { IPATCH_SF2_GEN_VOL_ENV_DELAY, "IPATCH_SF2_GEN_VOL_ENV_DELAY", "vol-env-delay" }, { IPATCH_SF2_GEN_VOL_ENV_ATTACK, "IPATCH_SF2_GEN_VOL_ENV_ATTACK", "vol-env-attack" }, { IPATCH_SF2_GEN_VOL_ENV_HOLD, "IPATCH_SF2_GEN_VOL_ENV_HOLD", "vol-env-hold" }, { IPATCH_SF2_GEN_VOL_ENV_DECAY, "IPATCH_SF2_GEN_VOL_ENV_DECAY", "vol-env-decay" }, { IPATCH_SF2_GEN_VOL_ENV_SUSTAIN, "IPATCH_SF2_GEN_VOL_ENV_SUSTAIN", "vol-env-sustain" }, { IPATCH_SF2_GEN_VOL_ENV_RELEASE, "IPATCH_SF2_GEN_VOL_ENV_RELEASE", "vol-env-release" }, { IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_HOLD, "IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_HOLD", "note-to-vol-env-hold" }, { IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_DECAY, "IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_DECAY", "note-to-vol-env-decay" }, { IPATCH_SF2_GEN_INSTRUMENT_ID, "IPATCH_SF2_GEN_INSTRUMENT_ID", "instrument-id" }, { IPATCH_SF2_GEN_RESERVED1, "IPATCH_SF2_GEN_RESERVED1", "reserved1" }, { IPATCH_SF2_GEN_NOTE_RANGE, "IPATCH_SF2_GEN_NOTE_RANGE", "note-range" }, { IPATCH_SF2_GEN_VELOCITY_RANGE, "IPATCH_SF2_GEN_VELOCITY_RANGE", "velocity-range" }, { IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START, "IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START", "sample-coarse-loop-start" }, { IPATCH_SF2_GEN_FIXED_NOTE, "IPATCH_SF2_GEN_FIXED_NOTE", "fixed-note" }, { IPATCH_SF2_GEN_FIXED_VELOCITY, "IPATCH_SF2_GEN_FIXED_VELOCITY", "fixed-velocity" }, { IPATCH_SF2_GEN_ATTENUATION, "IPATCH_SF2_GEN_ATTENUATION", "attenuation" }, { IPATCH_SF2_GEN_RESERVED2, "IPATCH_SF2_GEN_RESERVED2", "reserved2" }, { IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END, "IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END", "sample-coarse-loop-end" }, { IPATCH_SF2_GEN_COARSE_TUNE, "IPATCH_SF2_GEN_COARSE_TUNE", "coarse-tune" }, { IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE, "IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE", "fine-tune-override" }, { IPATCH_SF2_GEN_SAMPLE_ID, "IPATCH_SF2_GEN_SAMPLE_ID", "sample-id" }, { IPATCH_SF2_GEN_SAMPLE_MODES, "IPATCH_SF2_GEN_SAMPLE_MODES", "sample-modes" }, { IPATCH_SF2_GEN_RESERVED3, "IPATCH_SF2_GEN_RESERVED3", "reserved3" }, { IPATCH_SF2_GEN_SCALE_TUNE, "IPATCH_SF2_GEN_SCALE_TUNE", "scale-tune" }, { IPATCH_SF2_GEN_EXCLUSIVE_CLASS, "IPATCH_SF2_GEN_EXCLUSIVE_CLASS", "exclusive-class" }, { IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE, "IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE", "root-note-override" }, { 0, NULL, NULL } }; GType ipatch_sf2_gen_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSF2GenType", _ipatch_sf2_gen_type_values); return type; } static const GEnumValue _ipatch_sf2_gen_sample_modes_values[] = { { IPATCH_SF2_GEN_SAMPLE_MODE_NOLOOP, "IPATCH_SF2_GEN_SAMPLE_MODE_NOLOOP", "noloop" }, { IPATCH_SF2_GEN_SAMPLE_MODE_LOOP, "IPATCH_SF2_GEN_SAMPLE_MODE_LOOP", "loop" }, { IPATCH_SF2_GEN_SAMPLE_MODE_UNUSED, "IPATCH_SF2_GEN_SAMPLE_MODE_UNUSED", "unused" }, { IPATCH_SF2_GEN_SAMPLE_MODE_LOOP_RELEASE, "IPATCH_SF2_GEN_SAMPLE_MODE_LOOP_RELEASE", "loop-release" }, { 0, NULL, NULL } }; GType ipatch_sf2_gen_sample_modes_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSF2GenSampleModes", _ipatch_sf2_gen_sample_modes_values); return type; } /* enumerations from "IpatchSF2Mod.h" */ static const GEnumValue _ipatch_sf2_mod_field_masks_values[] = { { IPATCH_SF2_MOD_MASK_CONTROL, "IPATCH_SF2_MOD_MASK_CONTROL", "control" }, { IPATCH_SF2_MOD_MASK_CC, "IPATCH_SF2_MOD_MASK_CC", "cc" }, { IPATCH_SF2_MOD_MASK_DIRECTION, "IPATCH_SF2_MOD_MASK_DIRECTION", "direction" }, { IPATCH_SF2_MOD_MASK_POLARITY, "IPATCH_SF2_MOD_MASK_POLARITY", "polarity" }, { IPATCH_SF2_MOD_MASK_TYPE, "IPATCH_SF2_MOD_MASK_TYPE", "type" }, { 0, NULL, NULL } }; GType ipatch_sf2_mod_field_masks_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSF2ModFieldMasks", _ipatch_sf2_mod_field_masks_values); return type; } static const GEnumValue _ipatch_sf2_mod_field_shifts_values[] = { { IPATCH_SF2_MOD_SHIFT_CONTROL, "IPATCH_SF2_MOD_SHIFT_CONTROL", "control" }, { IPATCH_SF2_MOD_SHIFT_CC, "IPATCH_SF2_MOD_SHIFT_CC", "cc" }, { IPATCH_SF2_MOD_SHIFT_DIRECTION, "IPATCH_SF2_MOD_SHIFT_DIRECTION", "direction" }, { IPATCH_SF2_MOD_SHIFT_POLARITY, "IPATCH_SF2_MOD_SHIFT_POLARITY", "polarity" }, { IPATCH_SF2_MOD_SHIFT_TYPE, "IPATCH_SF2_MOD_SHIFT_TYPE", "type" }, { 0, NULL, NULL } }; GType ipatch_sf2_mod_field_shifts_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSF2ModFieldShifts", _ipatch_sf2_mod_field_shifts_values); return type; } static const GEnumValue _ipatch_sf2_mod_control_values[] = { { IPATCH_SF2_MOD_CONTROL_NONE, "IPATCH_SF2_MOD_CONTROL_NONE", "none" }, { IPATCH_SF2_MOD_CONTROL_NOTE_ON_VELOCITY, "IPATCH_SF2_MOD_CONTROL_NOTE_ON_VELOCITY", "note-on-velocity" }, { IPATCH_SF2_MOD_CONTROL_NOTE_NUMBER, "IPATCH_SF2_MOD_CONTROL_NOTE_NUMBER", "note-number" }, { IPATCH_SF2_MOD_CONTROL_POLY_PRESSURE, "IPATCH_SF2_MOD_CONTROL_POLY_PRESSURE", "poly-pressure" }, { IPATCH_SF2_MOD_CONTROL_CHAN_PRESSURE, "IPATCH_SF2_MOD_CONTROL_CHAN_PRESSURE", "chan-pressure" }, { IPATCH_SF2_MOD_CONTROL_PITCH_WHEEL, "IPATCH_SF2_MOD_CONTROL_PITCH_WHEEL", "pitch-wheel" }, { IPATCH_SF2_MOD_CONTROL_BEND_RANGE, "IPATCH_SF2_MOD_CONTROL_BEND_RANGE", "bend-range" }, { 0, NULL, NULL } }; GType ipatch_sf2_mod_control_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSF2ModControl", _ipatch_sf2_mod_control_values); return type; } static const GFlagsValue _ipatch_sf2_mod_control_palette_values[] = { { IPATCH_SF2_MOD_CC_GENERAL, "IPATCH_SF2_MOD_CC_GENERAL", "general" }, { IPATCH_SF2_MOD_CC_MIDI, "IPATCH_SF2_MOD_CC_MIDI", "midi" }, { 0, NULL, NULL } }; GType ipatch_sf2_mod_control_palette_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchSF2ModControlPalette", _ipatch_sf2_mod_control_palette_values); return type; } static const GFlagsValue _ipatch_sf2_mod_direction_values[] = { { IPATCH_SF2_MOD_DIRECTION_POSITIVE, "IPATCH_SF2_MOD_DIRECTION_POSITIVE", "positive" }, { IPATCH_SF2_MOD_DIRECTION_NEGATIVE, "IPATCH_SF2_MOD_DIRECTION_NEGATIVE", "negative" }, { 0, NULL, NULL } }; GType ipatch_sf2_mod_direction_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchSF2ModDirection", _ipatch_sf2_mod_direction_values); return type; } static const GFlagsValue _ipatch_sf2_mod_polarity_values[] = { { IPATCH_SF2_MOD_POLARITY_UNIPOLAR, "IPATCH_SF2_MOD_POLARITY_UNIPOLAR", "unipolar" }, { IPATCH_SF2_MOD_POLARITY_BIPOLAR, "IPATCH_SF2_MOD_POLARITY_BIPOLAR", "bipolar" }, { 0, NULL, NULL } }; GType ipatch_sf2_mod_polarity_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchSF2ModPolarity", _ipatch_sf2_mod_polarity_values); return type; } static const GFlagsValue _ipatch_sf2_mod_type_values[] = { { IPATCH_SF2_MOD_TYPE_LINEAR, "IPATCH_SF2_MOD_TYPE_LINEAR", "linear" }, { IPATCH_SF2_MOD_TYPE_CONCAVE, "IPATCH_SF2_MOD_TYPE_CONCAVE", "concave" }, { IPATCH_SF2_MOD_TYPE_CONVEX, "IPATCH_SF2_MOD_TYPE_CONVEX", "convex" }, { IPATCH_SF2_MOD_TYPE_SWITCH, "IPATCH_SF2_MOD_TYPE_SWITCH", "switch" }, { 0, NULL, NULL } }; GType ipatch_sf2_mod_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchSF2ModType", _ipatch_sf2_mod_type_values); return type; } static const GEnumValue _ipatch_sf2_mod_transform_values[] = { { IPATCH_SF2_MOD_TRANSFORM_LINEAR, "IPATCH_SF2_MOD_TRANSFORM_LINEAR", "linear" }, { 0, NULL, NULL } }; GType ipatch_sf2_mod_transform_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSF2ModTransform", _ipatch_sf2_mod_transform_values); return type; } static const GFlagsValue _ipatch_sf2_mod_flags_values[] = { { IPATCH_SF2_MOD_NO_DUPLICATE, "IPATCH_SF2_MOD_NO_DUPLICATE", "duplicate" }, { IPATCH_SF2_MOD_NO_NOTIFY, "IPATCH_SF2_MOD_NO_NOTIFY", "notify" }, { 0, NULL, NULL } }; GType ipatch_sf2_mod_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchSF2ModFlags", _ipatch_sf2_mod_flags_values); return type; } /* enumerations from "IpatchSF2Sample.h" */ static const GEnumValue _ipatch_sf2_sample_channel_values[] = { { IPATCH_SF2_SAMPLE_CHANNEL_MONO, "IPATCH_SF2_SAMPLE_CHANNEL_MONO", "mono" }, { IPATCH_SF2_SAMPLE_CHANNEL_LEFT, "IPATCH_SF2_SAMPLE_CHANNEL_LEFT", "left" }, { IPATCH_SF2_SAMPLE_CHANNEL_RIGHT, "IPATCH_SF2_SAMPLE_CHANNEL_RIGHT", "right" }, { 0, NULL, NULL } }; GType ipatch_sf2_sample_channel_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSF2SampleChannel", _ipatch_sf2_sample_channel_values); return type; } static const GFlagsValue _ipatch_sf2_sample_flags_values[] = { { IPATCH_SF2_SAMPLE_FLAG_ROM, "IPATCH_SF2_SAMPLE_FLAG_ROM", "rom" }, { 0, NULL, NULL } }; GType ipatch_sf2_sample_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchSF2SampleFlags", _ipatch_sf2_sample_flags_values); return type; } /* enumerations from "IpatchSF2VoiceCache.h" */ static const GEnumValue _ipatch_sf2_voice_sel_type_values[] = { { IPATCH_SF2_VOICE_SEL_NOTE, "IPATCH_SF2_VOICE_SEL_NOTE", "note" }, { IPATCH_SF2_VOICE_SEL_VELOCITY, "IPATCH_SF2_VOICE_SEL_VELOCITY", "velocity" }, { IPATCH_SF2_VOICE_SEL_AFTER_TOUCH, "IPATCH_SF2_VOICE_SEL_AFTER_TOUCH", "after-touch" }, { IPATCH_SF2_VOICE_SEL_MIDI_CC, "IPATCH_SF2_VOICE_SEL_MIDI_CC", "midi-cc" }, { 0, NULL, NULL } }; GType ipatch_sf2_voice_sel_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSF2VoiceSelType", _ipatch_sf2_voice_sel_type_values); return type; } /* enumerations from "IpatchSample.h" */ static const GEnumValue _ipatch_sample_loop_type_values[] = { { IPATCH_SAMPLE_LOOP_NONE, "IPATCH_SAMPLE_LOOP_NONE", "none" }, { IPATCH_SAMPLE_LOOP_STANDARD, "IPATCH_SAMPLE_LOOP_STANDARD", "standard" }, { IPATCH_SAMPLE_LOOP_RELEASE, "IPATCH_SAMPLE_LOOP_RELEASE", "release" }, { IPATCH_SAMPLE_LOOP_PINGPONG, "IPATCH_SAMPLE_LOOP_PINGPONG", "pingpong" }, { 0, NULL, NULL } }; GType ipatch_sample_loop_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSampleLoopType", _ipatch_sample_loop_type_values); return type; } /* enumerations from "IpatchSampleData.h" */ static const GFlagsValue _ipatch_sample_data_migrate_flags_values[] = { { IPATCH_SAMPLE_DATA_MIGRATE_REMOVE_NEW_IF_UNUSED, "IPATCH_SAMPLE_DATA_MIGRATE_REMOVE_NEW_IF_UNUSED", "remove-new-if-unused" }, { IPATCH_SAMPLE_DATA_MIGRATE_TO_NEWFILE, "IPATCH_SAMPLE_DATA_MIGRATE_TO_NEWFILE", "to-newfile" }, { IPATCH_SAMPLE_DATA_MIGRATE_LEAVE_IN_SWAP, "IPATCH_SAMPLE_DATA_MIGRATE_LEAVE_IN_SWAP", "leave-in-swap" }, { IPATCH_SAMPLE_DATA_MIGRATE_REPLACE, "IPATCH_SAMPLE_DATA_MIGRATE_REPLACE", "replace" }, { 0, NULL, NULL } }; GType ipatch_sample_data_migrate_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchSampleDataMigrateFlags", _ipatch_sample_data_migrate_flags_values); return type; } /* enumerations from "IpatchSampleStoreRam.h" */ static const GFlagsValue _ipatch_sample_store_ram_flags_values[] = { { IPATCH_SAMPLE_STORE_RAM_ALLOCATED, "IPATCH_SAMPLE_STORE_RAM_ALLOCATED", "allocated" }, { 0, NULL, NULL } }; GType ipatch_sample_store_ram_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchSampleStoreRamFlags", _ipatch_sample_store_ram_flags_values); return type; } /* enumerations from "IpatchSndFile.h" */ static const GEnumValue _ipatch_snd_file_endian_values[] = { { IPATCH_SND_FILE_ENDIAN_FILE, "IPATCH_SND_FILE_ENDIAN_FILE", "file" }, { IPATCH_SND_FILE_ENDIAN_LITTLE, "IPATCH_SND_FILE_ENDIAN_LITTLE", "little" }, { IPATCH_SND_FILE_ENDIAN_BIG, "IPATCH_SND_FILE_ENDIAN_BIG", "big" }, { IPATCH_SND_FILE_ENDIAN_CPU, "IPATCH_SND_FILE_ENDIAN_CPU", "cpu" }, { 0, NULL, NULL } }; GType ipatch_snd_file_endian_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSndFileEndian", _ipatch_snd_file_endian_values); return type; } /* enumerations from "IpatchTypeProp.h" */ static const GEnumValue _ipatch_splits_type_values[] = { { IPATCH_SPLITS_NONE, "IPATCH_SPLITS_NONE", "none" }, { IPATCH_SPLITS_NORMAL, "IPATCH_SPLITS_NORMAL", "normal" }, { IPATCH_SPLITS_NO_OVERLAP, "IPATCH_SPLITS_NO_OVERLAP", "no-overlap" }, { 0, NULL, NULL } }; GType ipatch_splits_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSplitsType", _ipatch_splits_type_values); return type; } /* enumerations from "IpatchUnit.h" */ static const GFlagsValue _ipatch_unit_flags_values[] = { { IPATCH_UNIT_LOGARITHMIC, "IPATCH_UNIT_LOGARITHMIC", "logarithmic" }, { IPATCH_UNIT_USER, "IPATCH_UNIT_USER", "user" }, { 0, NULL, NULL } }; GType ipatch_unit_flags_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchUnitFlags", _ipatch_unit_flags_values); return type; } static const GEnumValue _ipatch_unit_type_values[] = { { IPATCH_UNIT_TYPE_NONE, "IPATCH_UNIT_TYPE_NONE", "none" }, { IPATCH_UNIT_TYPE_INT, "IPATCH_UNIT_TYPE_INT", "int" }, { IPATCH_UNIT_TYPE_UINT, "IPATCH_UNIT_TYPE_UINT", "uint" }, { IPATCH_UNIT_TYPE_RANGE, "IPATCH_UNIT_TYPE_RANGE", "range" }, { IPATCH_UNIT_TYPE_DECIBELS, "IPATCH_UNIT_TYPE_DECIBELS", "decibels" }, { IPATCH_UNIT_TYPE_PERCENT, "IPATCH_UNIT_TYPE_PERCENT", "percent" }, { IPATCH_UNIT_TYPE_SEMITONES, "IPATCH_UNIT_TYPE_SEMITONES", "semitones" }, { IPATCH_UNIT_TYPE_CENTS, "IPATCH_UNIT_TYPE_CENTS", "cents" }, { IPATCH_UNIT_TYPE_TIME_CENTS, "IPATCH_UNIT_TYPE_TIME_CENTS", "time-cents" }, { IPATCH_UNIT_TYPE_SAMPLE_RATE, "IPATCH_UNIT_TYPE_SAMPLE_RATE", "sample-rate" }, { IPATCH_UNIT_TYPE_SAMPLES, "IPATCH_UNIT_TYPE_SAMPLES", "samples" }, { IPATCH_UNIT_TYPE_HERTZ, "IPATCH_UNIT_TYPE_HERTZ", "hertz" }, { IPATCH_UNIT_TYPE_SECONDS, "IPATCH_UNIT_TYPE_SECONDS", "seconds" }, { IPATCH_UNIT_TYPE_MULTIPLIER, "IPATCH_UNIT_TYPE_MULTIPLIER", "multiplier" }, { IPATCH_UNIT_TYPE_DLS_GAIN, "IPATCH_UNIT_TYPE_DLS_GAIN", "dls-gain" }, { IPATCH_UNIT_TYPE_DLS_ABS_TIME, "IPATCH_UNIT_TYPE_DLS_ABS_TIME", "dls-abs-time" }, { IPATCH_UNIT_TYPE_DLS_REL_TIME, "IPATCH_UNIT_TYPE_DLS_REL_TIME", "dls-rel-time" }, { IPATCH_UNIT_TYPE_DLS_ABS_PITCH, "IPATCH_UNIT_TYPE_DLS_ABS_PITCH", "dls-abs-pitch" }, { IPATCH_UNIT_TYPE_DLS_REL_PITCH, "IPATCH_UNIT_TYPE_DLS_REL_PITCH", "dls-rel-pitch" }, { IPATCH_UNIT_TYPE_DLS_PERCENT, "IPATCH_UNIT_TYPE_DLS_PERCENT", "dls-percent" }, { IPATCH_UNIT_TYPE_SF2_ABS_PITCH, "IPATCH_UNIT_TYPE_SF2_ABS_PITCH", "sf2-abs-pitch" }, { IPATCH_UNIT_TYPE_SF2_OFS_PITCH, "IPATCH_UNIT_TYPE_SF2_OFS_PITCH", "sf2-ofs-pitch" }, { IPATCH_UNIT_TYPE_SF2_ABS_TIME, "IPATCH_UNIT_TYPE_SF2_ABS_TIME", "sf2-abs-time" }, { IPATCH_UNIT_TYPE_SF2_OFS_TIME, "IPATCH_UNIT_TYPE_SF2_OFS_TIME", "sf2-ofs-time" }, { IPATCH_UNIT_TYPE_CENTIBELS, "IPATCH_UNIT_TYPE_CENTIBELS", "centibels" }, { IPATCH_UNIT_TYPE_32K_SAMPLES, "IPATCH_UNIT_TYPE_32K_SAMPLES", "32k-samples" }, { IPATCH_UNIT_TYPE_TENTH_PERCENT, "IPATCH_UNIT_TYPE_TENTH_PERCENT", "tenth-percent" }, { 0, NULL, NULL } }; GType ipatch_unit_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchUnitType", _ipatch_unit_type_values); return type; } static const GEnumValue _ipatch_unit_class_type_values[] = { { IPATCH_UNIT_CLASS_NONE, "IPATCH_UNIT_CLASS_NONE", "none" }, { IPATCH_UNIT_CLASS_USER, "IPATCH_UNIT_CLASS_USER", "user" }, { IPATCH_UNIT_CLASS_DLS, "IPATCH_UNIT_CLASS_DLS", "dls" }, { IPATCH_UNIT_CLASS_COUNT, "IPATCH_UNIT_CLASS_COUNT", "count" }, { 0, NULL, NULL } }; GType ipatch_unit_class_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchUnitClassType", _ipatch_unit_class_type_values); return type; } /* enumerations from "IpatchVBankRegion.h" */ static const GEnumValue _ipatch_vbank_region_note_range_mode_values[] = { { IPATCH_VBANK_REGION_NOTE_RANGE_MODE_INTERSECT, "IPATCH_VBANK_REGION_NOTE_RANGE_MODE_INTERSECT", "intersect" }, { IPATCH_VBANK_REGION_NOTE_RANGE_MODE_OVERRIDE, "IPATCH_VBANK_REGION_NOTE_RANGE_MODE_OVERRIDE", "override" }, { 0, NULL, NULL } }; GType ipatch_vbank_region_note_range_mode_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchVBankRegionNoteRangeMode", _ipatch_vbank_region_note_range_mode_values); return type; } static const GEnumValue _ipatch_vbank_region_root_note_mode_values[] = { { IPATCH_VBANK_REGION_ROOT_NOTE_MODE_OFFSET, "IPATCH_VBANK_REGION_ROOT_NOTE_MODE_OFFSET", "offset" }, { IPATCH_VBANK_REGION_ROOT_NOTE_MODE_OVERRIDE, "IPATCH_VBANK_REGION_ROOT_NOTE_MODE_OVERRIDE", "override" }, { 0, NULL, NULL } }; GType ipatch_vbank_region_root_note_mode_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchVBankRegionRootNoteMode", _ipatch_vbank_region_root_note_mode_values); return type; } /* enumerations from "misc.h" */ static const GEnumValue _ipatch_error_values[] = { { IPATCH_ERROR_FAIL, "IPATCH_ERROR_FAIL", "fail" }, { IPATCH_ERROR_IO, "IPATCH_ERROR_IO", "io" }, { IPATCH_ERROR_PROGRAM, "IPATCH_ERROR_PROGRAM", "program" }, { IPATCH_ERROR_INVALID, "IPATCH_ERROR_INVALID", "invalid" }, { IPATCH_ERROR_CORRUPT, "IPATCH_ERROR_CORRUPT", "corrupt" }, { IPATCH_ERROR_NOMEM, "IPATCH_ERROR_NOMEM", "nomem" }, { IPATCH_ERROR_UNSUPPORTED, "IPATCH_ERROR_UNSUPPORTED", "unsupported" }, { IPATCH_ERROR_UNEXPECTED_EOF, "IPATCH_ERROR_UNEXPECTED_EOF", "unexpected-eof" }, { IPATCH_ERROR_UNHANDLED_CONVERSION, "IPATCH_ERROR_UNHANDLED_CONVERSION", "unhandled-conversion" }, { IPATCH_ERROR_BUSY, "IPATCH_ERROR_BUSY", "busy" }, { 0, NULL, NULL } }; GType ipatch_error_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchError", _ipatch_error_values); return type; } /* enumerations from "sample.h" */ static const GEnumValue _ipatch_sample_width_values[] = { { IPATCH_SAMPLE_INVALID, "IPATCH_SAMPLE_INVALID", "invalid" }, { IPATCH_SAMPLE_BIT8, "IPATCH_SAMPLE_BIT8", "bit8" }, { IPATCH_SAMPLE_BIT16, "IPATCH_SAMPLE_BIT16", "bit16" }, { IPATCH_SAMPLE_BIT24, "IPATCH_SAMPLE_BIT24", "bit24" }, { IPATCH_SAMPLE_BIT32, "IPATCH_SAMPLE_BIT32", "bit32" }, { IPATCH_SAMPLE_FLOAT, "IPATCH_SAMPLE_FLOAT", "float" }, { IPATCH_SAMPLE_DOUBLE, "IPATCH_SAMPLE_DOUBLE", "double" }, { IPATCH_SAMPLE_REAL24BIT, "IPATCH_SAMPLE_REAL24BIT", "real24bit" }, { 0, NULL, NULL } }; GType ipatch_sample_width_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSampleWidth", _ipatch_sample_width_values); return type; } static const GFlagsValue _ipatch_sample_channel_values[] = { { IPATCH_SAMPLE_MONO, "IPATCH_SAMPLE_MONO", "mono" }, { IPATCH_SAMPLE_STEREO, "IPATCH_SAMPLE_STEREO", "stereo" }, { 0, NULL, NULL } }; GType ipatch_sample_channel_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchSampleChannel", _ipatch_sample_channel_values); return type; } static const GEnumValue _ipatch_sample_channel_type_values[] = { { IPATCH_SAMPLE_LEFT, "IPATCH_SAMPLE_LEFT", "left" }, { IPATCH_SAMPLE_RIGHT, "IPATCH_SAMPLE_RIGHT", "right" }, { 0, NULL, NULL } }; GType ipatch_sample_channel_type_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_enum_register_static ("IpatchSampleChannelType", _ipatch_sample_channel_type_values); return type; } static const GFlagsValue _ipatch_sample_sign_values[] = { { IPATCH_SAMPLE_SIGNED, "IPATCH_SAMPLE_SIGNED", "signed" }, { IPATCH_SAMPLE_UNSIGNED, "IPATCH_SAMPLE_UNSIGNED", "unsigned" }, { 0, NULL, NULL } }; GType ipatch_sample_sign_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchSampleSign", _ipatch_sample_sign_values); return type; } static const GFlagsValue _ipatch_sample_endian_values[] = { { IPATCH_SAMPLE_LENDIAN, "IPATCH_SAMPLE_LENDIAN", "lendian" }, { IPATCH_SAMPLE_BENDIAN, "IPATCH_SAMPLE_BENDIAN", "bendian" }, { 0, NULL, NULL } }; GType ipatch_sample_endian_get_type (void) { static GType type = 0; if (G_UNLIKELY (type == 0)) type = g_flags_register_static ("IpatchSampleEndian", _ipatch_sample_endian_values); return type; } /* Generated data ends here */ libinstpatch-1.1.6/libinstpatch/builtin_enums.h000066400000000000000000000227541400263525300217270ustar00rootroot00000000000000 /* This file is generated by glib-mkenums, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */ #ifndef __IPATCH_BUILTIN_ENUMS_H__ #define __IPATCH_BUILTIN_ENUMS_H__ #include G_BEGIN_DECLS /* enumerations from "IpatchBase.h" */ GType ipatch_base_flags_get_type (void); #define IPATCH_TYPE_BASE_FLAGS (ipatch_base_flags_get_type()) /* enumerations from "IpatchConverter.h" */ GType ipatch_converter_log_type_get_type (void); #define IPATCH_TYPE_CONVERTER_LOG_TYPE (ipatch_converter_log_type_get_type()) GType ipatch_converter_count_get_type (void); #define IPATCH_TYPE_CONVERTER_COUNT (ipatch_converter_count_get_type()) GType ipatch_converter_flags_get_type (void); #define IPATCH_TYPE_CONVERTER_FLAGS (ipatch_converter_flags_get_type()) GType ipatch_converter_priority_get_type (void); #define IPATCH_TYPE_CONVERTER_PRIORITY (ipatch_converter_priority_get_type()) /* enumerations from "IpatchDLS2.h" */ GType ipatch_dls2_flags_get_type (void); #define IPATCH_TYPE_DLS2_FLAGS (ipatch_dls2_flags_get_type()) /* enumerations from "IpatchDLS2Conn.h" */ GType ipatch_dls2_conn_src_type_get_type (void); #define IPATCH_TYPE_DLS2_CONN_SRC_TYPE (ipatch_dls2_conn_src_type_get_type()) GType ipatch_dls2_conn_dest_type_get_type (void); #define IPATCH_TYPE_DLS2_CONN_DEST_TYPE (ipatch_dls2_conn_dest_type_get_type()) GType ipatch_dls2_conn_transform_type_get_type (void); #define IPATCH_TYPE_DLS2_CONN_TRANSFORM_TYPE (ipatch_dls2_conn_transform_type_get_type()) GType ipatch_dls2_conn_polarity_type_get_type (void); #define IPATCH_TYPE_DLS2_CONN_POLARITY_TYPE (ipatch_dls2_conn_polarity_type_get_type()) GType ipatch_dls2_conn_transform_masks_get_type (void); #define IPATCH_TYPE_DLS2_CONN_TRANSFORM_MASKS (ipatch_dls2_conn_transform_masks_get_type()) GType ipatch_dls2_conn_transform_shifts_get_type (void); #define IPATCH_TYPE_DLS2_CONN_TRANSFORM_SHIFTS (ipatch_dls2_conn_transform_shifts_get_type()) /* enumerations from "IpatchDLS2Info.h" */ GType ipatch_dls2_info_type_get_type (void); #define IPATCH_TYPE_DLS2_INFO_TYPE (ipatch_dls2_info_type_get_type()) /* enumerations from "IpatchDLS2Inst.h" */ GType ipatch_dls2_inst_flags_get_type (void); #define IPATCH_TYPE_DLS2_INST_FLAGS (ipatch_dls2_inst_flags_get_type()) /* enumerations from "IpatchDLS2Region.h" */ GType ipatch_dls2_param_get_type (void); #define IPATCH_TYPE_DLS2_PARAM (ipatch_dls2_param_get_type()) GType ipatch_dls2_region_channel_type_get_type (void); #define IPATCH_TYPE_DLS2_REGION_CHANNEL_TYPE (ipatch_dls2_region_channel_type_get_type()) GType ipatch_dls2_region_flags_get_type (void); #define IPATCH_TYPE_DLS2_REGION_FLAGS (ipatch_dls2_region_flags_get_type()) /* enumerations from "IpatchDLS2Sample.h" */ GType ipatch_dls2_sample_flags_get_type (void); #define IPATCH_TYPE_DLS2_SAMPLE_FLAGS (ipatch_dls2_sample_flags_get_type()) /* enumerations from "IpatchDLSReader.h" */ GType ipatch_dls_reader_error_get_type (void); #define IPATCH_TYPE_DLS_READER_ERROR (ipatch_dls_reader_error_get_type()) /* enumerations from "IpatchFile.h" */ GType ipatch_file_flags_get_type (void); #define IPATCH_TYPE_FILE_FLAGS (ipatch_file_flags_get_type()) GType ipatch_file_identify_order_get_type (void); #define IPATCH_TYPE_FILE_IDENTIFY_ORDER (ipatch_file_identify_order_get_type()) /* enumerations from "IpatchGigDimension.h" */ GType ipatch_gig_dimension_type_get_type (void); #define IPATCH_TYPE_GIG_DIMENSION_TYPE (ipatch_gig_dimension_type_get_type()) /* enumerations from "IpatchGigEffects.h" */ GType ipatch_gig_filter_type_get_type (void); #define IPATCH_TYPE_GIG_FILTER_TYPE (ipatch_gig_filter_type_get_type()) GType ipatch_gig_control_type_get_type (void); #define IPATCH_TYPE_GIG_CONTROL_TYPE (ipatch_gig_control_type_get_type()) /* enumerations from "IpatchGigRegion.h" */ GType ipatch_gig_region_flags_get_type (void); #define IPATCH_TYPE_GIG_REGION_FLAGS (ipatch_gig_region_flags_get_type()) /* enumerations from "IpatchGigSubRegion.h" */ GType ipatch_gig_sub_region_flags_get_type (void); #define IPATCH_TYPE_GIG_SUB_REGION_FLAGS (ipatch_gig_sub_region_flags_get_type()) /* enumerations from "IpatchItem.h" */ GType ipatch_item_flags_get_type (void); #define IPATCH_TYPE_ITEM_FLAGS (ipatch_item_flags_get_type()) /* enumerations from "IpatchPaste.h" */ GType ipatch_paste_choice_get_type (void); #define IPATCH_TYPE_PASTE_CHOICE (ipatch_paste_choice_get_type()) GType ipatch_paste_priority_get_type (void); #define IPATCH_TYPE_PASTE_PRIORITY (ipatch_paste_priority_get_type()) /* enumerations from "IpatchRiff.h" */ GType ipatch_riff_status_get_type (void); #define IPATCH_TYPE_RIFF_STATUS (ipatch_riff_status_get_type()) GType ipatch_riff_mode_get_type (void); #define IPATCH_TYPE_RIFF_MODE (ipatch_riff_mode_get_type()) GType ipatch_riff_chunk_type_get_type (void); #define IPATCH_TYPE_RIFF_CHUNK_TYPE (ipatch_riff_chunk_type_get_type()) GType ipatch_riff_error_get_type (void); #define IPATCH_TYPE_RIFF_ERROR (ipatch_riff_error_get_type()) /* enumerations from "IpatchSF2.h" */ GType ipatch_sf2_flags_get_type (void); #define IPATCH_TYPE_SF2_FLAGS (ipatch_sf2_flags_get_type()) GType ipatch_sf2_info_type_get_type (void); #define IPATCH_TYPE_SF2_INFO_TYPE (ipatch_sf2_info_type_get_type()) /* enumerations from "IpatchSF2Gen.h" */ GType ipatch_sf2_gen_props_type_get_type (void); #define IPATCH_TYPE_SF2_GEN_PROPS_TYPE (ipatch_sf2_gen_props_type_get_type()) GType ipatch_sf2_gen_type_get_type (void); #define IPATCH_TYPE_SF2_GEN_TYPE (ipatch_sf2_gen_type_get_type()) GType ipatch_sf2_gen_sample_modes_get_type (void); #define IPATCH_TYPE_SF2_GEN_SAMPLE_MODES (ipatch_sf2_gen_sample_modes_get_type()) /* enumerations from "IpatchSF2Mod.h" */ GType ipatch_sf2_mod_field_masks_get_type (void); #define IPATCH_TYPE_SF2_MOD_FIELD_MASKS (ipatch_sf2_mod_field_masks_get_type()) GType ipatch_sf2_mod_field_shifts_get_type (void); #define IPATCH_TYPE_SF2_MOD_FIELD_SHIFTS (ipatch_sf2_mod_field_shifts_get_type()) GType ipatch_sf2_mod_control_get_type (void); #define IPATCH_TYPE_SF2_MOD_CONTROL (ipatch_sf2_mod_control_get_type()) GType ipatch_sf2_mod_control_palette_get_type (void); #define IPATCH_TYPE_SF2_MOD_CONTROL_PALETTE (ipatch_sf2_mod_control_palette_get_type()) GType ipatch_sf2_mod_direction_get_type (void); #define IPATCH_TYPE_SF2_MOD_DIRECTION (ipatch_sf2_mod_direction_get_type()) GType ipatch_sf2_mod_polarity_get_type (void); #define IPATCH_TYPE_SF2_MOD_POLARITY (ipatch_sf2_mod_polarity_get_type()) GType ipatch_sf2_mod_type_get_type (void); #define IPATCH_TYPE_SF2_MOD_TYPE (ipatch_sf2_mod_type_get_type()) GType ipatch_sf2_mod_transform_get_type (void); #define IPATCH_TYPE_SF2_MOD_TRANSFORM (ipatch_sf2_mod_transform_get_type()) GType ipatch_sf2_mod_flags_get_type (void); #define IPATCH_TYPE_SF2_MOD_FLAGS (ipatch_sf2_mod_flags_get_type()) /* enumerations from "IpatchSF2Sample.h" */ GType ipatch_sf2_sample_channel_get_type (void); #define IPATCH_TYPE_SF2_SAMPLE_CHANNEL (ipatch_sf2_sample_channel_get_type()) GType ipatch_sf2_sample_flags_get_type (void); #define IPATCH_TYPE_SF2_SAMPLE_FLAGS (ipatch_sf2_sample_flags_get_type()) /* enumerations from "IpatchSF2VoiceCache.h" */ GType ipatch_sf2_voice_sel_type_get_type (void); #define IPATCH_TYPE_SF2_VOICE_SEL_TYPE (ipatch_sf2_voice_sel_type_get_type()) /* enumerations from "IpatchSample.h" */ GType ipatch_sample_loop_type_get_type (void); #define IPATCH_TYPE_SAMPLE_LOOP_TYPE (ipatch_sample_loop_type_get_type()) /* enumerations from "IpatchSampleData.h" */ GType ipatch_sample_data_migrate_flags_get_type (void); #define IPATCH_TYPE_SAMPLE_DATA_MIGRATE_FLAGS (ipatch_sample_data_migrate_flags_get_type()) /* enumerations from "IpatchSampleStoreRam.h" */ GType ipatch_sample_store_ram_flags_get_type (void); #define IPATCH_TYPE_SAMPLE_STORE_RAM_FLAGS (ipatch_sample_store_ram_flags_get_type()) /* enumerations from "IpatchSndFile.h" */ GType ipatch_snd_file_endian_get_type (void); #define IPATCH_TYPE_SND_FILE_ENDIAN (ipatch_snd_file_endian_get_type()) /* enumerations from "IpatchTypeProp.h" */ GType ipatch_splits_type_get_type (void); #define IPATCH_TYPE_SPLITS_TYPE (ipatch_splits_type_get_type()) /* enumerations from "IpatchUnit.h" */ GType ipatch_unit_flags_get_type (void); #define IPATCH_TYPE_UNIT_FLAGS (ipatch_unit_flags_get_type()) GType ipatch_unit_type_get_type (void); #define IPATCH_TYPE_UNIT_TYPE (ipatch_unit_type_get_type()) GType ipatch_unit_class_type_get_type (void); #define IPATCH_TYPE_UNIT_CLASS_TYPE (ipatch_unit_class_type_get_type()) /* enumerations from "IpatchVBankRegion.h" */ GType ipatch_vbank_region_note_range_mode_get_type (void); #define IPATCH_TYPE_VBANK_REGION_NOTE_RANGE_MODE (ipatch_vbank_region_note_range_mode_get_type()) GType ipatch_vbank_region_root_note_mode_get_type (void); #define IPATCH_TYPE_VBANK_REGION_ROOT_NOTE_MODE (ipatch_vbank_region_root_note_mode_get_type()) /* enumerations from "misc.h" */ GType ipatch_error_get_type (void); #define IPATCH_TYPE_ERROR (ipatch_error_get_type()) /* enumerations from "sample.h" */ GType ipatch_sample_width_get_type (void); #define IPATCH_TYPE_SAMPLE_WIDTH (ipatch_sample_width_get_type()) GType ipatch_sample_channel_get_type (void); #define IPATCH_TYPE_SAMPLE_CHANNEL (ipatch_sample_channel_get_type()) GType ipatch_sample_channel_type_get_type (void); #define IPATCH_TYPE_SAMPLE_CHANNEL_TYPE (ipatch_sample_channel_type_get_type()) GType ipatch_sample_sign_get_type (void); #define IPATCH_TYPE_SAMPLE_SIGN (ipatch_sample_sign_get_type()) GType ipatch_sample_endian_get_type (void); #define IPATCH_TYPE_SAMPLE_ENDIAN (ipatch_sample_endian_get_type()) G_END_DECLS #endif /* __IPATCH_BUILTIN_ENUMS_H__ */ /* Generated data ends here */ libinstpatch-1.1.6/libinstpatch/compat.c000066400000000000000000000043441400263525300203230ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #include "compat.h" #ifdef IPATCH_COMPAT_GWEAKREF /* Older glib implementation of GWeakRef (not thread safe!) */ /** * g_weak_ref_init: (skip) */ void g_weak_ref_init(GWeakRef *weak_ref, gpointer object) { weak_ref->obj = object; if(object) { g_object_add_weak_pointer((GObject *)object, &weak_ref->obj); } } /** * g_weak_ref_clear: (skip) */ void g_weak_ref_clear(GWeakRef *weak_ref) { GObject *object; object = weak_ref->obj; if(object) { g_object_ref(object); // ++ ref g_object_remove_weak_pointer((GObject *)object, &weak_ref->obj); g_object_unref(object); // -- unref weak_ref->obj = NULL; } } /** * g_weak_ref_get: (skip) */ gpointer g_weak_ref_get(GWeakRef *weak_ref) { GObject *object; object = weak_ref->obj; if(object) { g_object_ref(object); // ++ ref } return (object); // !! caller takes over reference } /** * g_weak_ref_set: (skip) */ void g_weak_ref_set(GWeakRef *weak_ref, gpointer object) { g_weak_ref_clear(weak_ref); g_weak_ref_init(weak_ref, object); } #endif #ifdef IPATCH_COMPAT_SLIST_FREE_FULL /** * g_slist_free_full: (skip) */ void g_slist_free_full(GSList *list, GDestroyNotify free_func) { GSList *p; for(p = list; p; p = g_slist_delete_link(p, p)) if(free_func) { free_func(p->data); } } #endif libinstpatch-1.1.6/libinstpatch/compat.h000066400000000000000000000030631400263525300203250ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /* Older glib compatibility functions */ #ifndef __LIBINSTPATCH_COMPAT_H__ #define __LIBINSTPATCH_COMPAT_H__ #include // Enable GWeakRef emulation if glib version less than 2.32 #ifndef GLIB_VERSION_2_32 #define IPATCH_COMPAT_GWEAKREF 1 typedef struct _GWeakRef { gpointer obj; } GWeakRef; void g_weak_ref_init(GWeakRef *weak_ref, gpointer object); void g_weak_ref_clear(GWeakRef *weak_ref); gpointer g_weak_ref_get(GWeakRef *weak_ref); void g_weak_ref_set(GWeakRef *weak_ref, gpointer object); #endif // Enable g_slist_free_full emulation if glib version less than 2.28 #ifndef GLIB_VERSION_2_28 #define IPATCH_COMPAT_SLIST_FREE_FULL 1 void g_slist_free_full(GSList *list, GDestroyNotify free_func); #endif #endif libinstpatch-1.1.6/libinstpatch/gir_prog.c000066400000000000000000000014461400263525300206500ustar00rootroot00000000000000#include #include #include int main(int argc, char *argv[]) { GError *err = NULL; char *irdump = NULL; GOptionContext *context; GOptionEntry entries[] = { { "introspect-dump", 'i', 0, G_OPTION_ARG_STRING, &irdump, NULL, NULL }, { NULL } }; context = g_option_context_new(NULL); g_option_context_add_main_entries(context, entries, "libinstpatch-gir-program"); if(!g_option_context_parse(context, &argc, &argv, &err)) { g_print("option parsing failed: %s\n", err->message); return (1); } ipatch_init(); if(!g_irepository_dump(irdump, &err)) { g_print("g_irepository_dump() failed: %s\n", err->message); return (1); } return (0); } libinstpatch-1.1.6/libinstpatch/i18n.h000066400000000000000000000005631400263525300176230ustar00rootroot00000000000000#ifndef __I18N_H__ #define __I18N_H__ #include #ifndef _ #if defined(ENABLE_NLS) # include # define _(x) dgettext(PACKAGE, x) # ifdef gettext_noop # define N_(String) gettext_noop (String) # else # define N_(String) (String) # endif #else # define N_(String) (String) # define _(x) (x) # define gettext(x) (x) #endif #endif #endif libinstpatch-1.1.6/libinstpatch/ipatch_priv.h000066400000000000000000000071451400263525300213570ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /* * ipatch_priv.h - Private header file */ #ifndef __IPATCH_PRIV_H__ #define __IPATCH_PRIV_H__ #include #include "IpatchItem.h" #include "marshals.h" #include "misc.h" #include "i18n.h" #if HAVE_IO_H #include // _lseek(), _close(), _read(), _write() on windows #endif #if HAVE_UNISTD_H #include #endif /* In case of cross compiling from Linux to Win32, unistd.h and io.h may be both present. So, we provide here exclusive macros definition for files i/o. */ #ifdef _WIN32 /* seek in file described by its file descriptor fd */ #define IPATCH_FD_LSEEK(fd, offset,origin) _lseek(fd, offset, origin) /* read from file described by its file descriptor fd */ #define IPATCH_FD_READ(fd, bufdst, count) _read(fd, bufdst, count) /* read to file described by its file descriptor fd */ #define IPATCH_FD_WRITE(fd, bufsrc, count) _write(fd, bufsrc, count) /* close a file described by its file descriptor fd */ #define IPATCH_FD_CLOSE(fd) _close(fd) #else #define IPATCH_FD_LSEEK(fd, offset,origin) lseek(fd, offset, origin) #define IPATCH_FD_READ(fd, bufdst, count) read(fd, bufdst, count) #define IPATCH_FD_WRITE(fd, bufsrc, count) write(fd, bufsrc, count) #define IPATCH_FD_CLOSE(fd) close(fd) #endif #define IPATCH_UNTITLED _("Untitled") /* macro for getting a GParamSpec property ID (FIXME - its a private field!) */ #define IPATCH_PARAM_SPEC_ID(pspec) ((pspec)->param_id) /* size of buffers used for transfering sample data (in bytes) Must be a multiple of 16 bytes */ #define IPATCH_SAMPLE_COPY_BUFFER_SIZE (32 * 1024) /* Size of transform buffers used by IpatchSampleTransform objects in pool */ #define IPATCH_SAMPLE_TRANS_BUFFER_SIZE (32 * 1024) /* size of buffers used for copying data */ #define IPATCH_COPY_BUFFER_SIZE (32 * 1024) /* So we can start using the glib 2.10 allocator now */ #ifndef g_slice_new #define g_slice_new(type) g_new (type, 1) #define g_slice_new0(type) g_new0 (type, 1) #define g_slice_free(type, mem) g_free (mem) #define g_slice_alloc(size) g_malloc(size) #define g_slice_free1(size, mem) g_free (mem) #endif /* can be used in place of g_assert_not_reached, for g_return(_val)_if_fail to print error and return instead of terminating program */ #define NOT_REACHED 0 #ifdef __GNUC__ #define log_if_fail(expr) (!(expr) && \ _ret_g_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \ "file %s: line %d (%s): assertion `%s' failed.", \ __FILE__, __LINE__, __PRETTY_FUNCTION__, \ #expr)) #else /* !GNUC */ #define log_if_fail(expr) (!(expr) && \ _ret_g_log (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, \ "file %s: line %d: assertion `%s' failed.", \ __FILE__, __LINE__, \ #expr)) #endif int _ret_g_log(const gchar *log_domain, GLogLevelFlags log_level, const gchar *format, ...); #endif libinstpatch-1.1.6/libinstpatch/libinstpatch.def000066400000000000000000000646561400263525300220540ustar00rootroot00000000000000EXPORTS ipatch_close _ipatch_code_error _ipatch_code_errorv _ipatch_convert_DLS2_init _ipatch_convert_SF2_init _ipatch_convert_gig_init ipatch_file_identify_new ipatch_file_identify_name ipatch_close_base_list ipatch_gerror_message ipatch_convert_object_to_type_multi_set ;_ipatch_cram_decoder_class_install_properties ;_ipatch_cram_encoder_class_install_properties ;_ipatch_cram_init ;_ipatch_dls2_conn_init ;_ipatch_dls2_info_init ;_ipatch_dls2_sample_init ;_ipatch_iface_prop_init _ipatch_param_init ;_ipatch_paste_init _ipatch_sf2_gen_init ;_ipatch_sf2_mod_init _ipatch_sf2_voice_cache_init_DLS _ipatch_sf2_voice_cache_init_SF2 _ipatch_sf2_voice_cache_init_gig _ipatch_type_prop_init _ipatch_unit_dls_init _ipatch_unit_generic_init ipatch_unit_info_new ipatch_unit_lookup _ipatch_unit_init _ipatch_unit_sf2_init ipatch_application_name DATA ;ipatch_audio_file_get_type ;ipatch_base_find_item_by_midi_locale_ref ipatch_base_type_get_mime_type ipatch_base_find_item_by_midi_locale ipatch_base_find_unused_midi_locale ;ipatch_base_flags_get_type ipatch_base_get_file_name ipatch_base_get_type ;ipatch_base_ref_file ipatch_base_set_file ipatch_base_set_file_name ipatch_container_add_unique ipatch_container_add_connect ipatch_container_add_disconnect_matched ipatch_container_append ipatch_container_count ipatch_container_get_child_types ipatch_container_get_children ipatch_container_get_children_list ipatch_container_get_type ipatch_container_get_virtual_types ipatch_container_init_iter ipatch_container_insert ipatch_container_insert_iter ipatch_container_make_unique ipatch_container_remove ipatch_container_remove_all ipatch_container_remove_connect ipatch_container_remove_disconnect_matched ipatch_container_remove_iter ;ipatch_container_set_add_hook ;ipatch_container_set_remove_hook ipatch_container_type_get_child_types ipatch_converter_add_input ipatch_convert_object_to_type ipatch_convert_objects ipatch_converter_add_inputs ipatch_converter_add_output ipatch_converter_add_outputs ipatch_converter_convert ipatch_converter_dls2_inst_to_sf2_voice_cache_get_type ipatch_converter_dls2_region_to_sf2_voice_cache_get_type ipatch_converter_dls2_sample_to_sf2_voice_cache_get_type ipatch_converter_dls2_to_file_get_type ipatch_converter_file_to_dls2_get_type ipatch_converter_file_to_gig_get_type ipatch_converter_file_to_sf2_get_type ;ipatch_converter_find_flags_get_type ;ipatch_converter_flags_get_type ipatch_converter_get_notes ipatch_converter_get_type ipatch_converter_gig_inst_to_sf2_voice_cache_get_type ipatch_converter_gig_sample_to_sf2_voice_cache_get_type ipatch_converter_gig_to_file_get_type ipatch_converter_init ipatch_converter_log ipatch_converter_log_next ;ipatch_converter_log_type_get_type ;ipatch_converter_ref_input ;ipatch_converter_ref_inputs ;ipatch_converter_ref_output ;ipatch_converter_ref_outputs ipatch_converter_reset ;ipatch_converter_sample_file_to_dls2_sample_get_type ;ipatch_converter_sample_file_to_sf2_sample_get_type ipatch_converter_sf2_inst_to_sf2_voice_cache_get_type ipatch_converter_sf2_izone_to_sf2_voice_cache_get_type ipatch_converter_sf2_preset_to_sf2_voice_cache_get_type ipatch_converter_sf2_pzone_to_sf2_voice_cache_get_type ipatch_converter_sf2_sample_to_sf2_voice_cache_get_type ipatch_converter_sf2_to_file_get_type ipatch_converter_verify ;ipatch_cram_close_ebml_chunk ;ipatch_cram_decoder_converter_get_type ;ipatch_cram_decoder_converter_new ;ipatch_cram_decoder_decode_all ;ipatch_cram_decoder_decode_file ;ipatch_cram_decoder_get_type ;ipatch_cram_decoder_new ;ipatch_cram_decoder_next_file ;ipatch_cram_decoder_set_cram_file ;ipatch_cram_decoder_set_dest_file ;ipatch_cram_decoder_start ;ipatch_cram_dls_file_encode ;ipatch_cram_encoder_converter_get_type ;ipatch_cram_encoder_converter_new ;ipatch_cram_encoder_declare_binary ;ipatch_cram_encoder_declare_file ;ipatch_cram_encoder_declare_flac_audio ;ipatch_cram_encoder_end ;ipatch_cram_encoder_flags_get_type ;ipatch_cram_encoder_get_type ;ipatch_cram_encoder_new ;ipatch_cram_encoder_set_audio_endian ;ipatch_cram_encoder_set_file ;ipatch_cram_encoder_start ;ipatch_cram_encoder_store_audio ;ipatch_cram_encoder_store_audio_interleaved ;ipatch_cram_encoder_store_binary ;ipatch_cram_file_get_type ;ipatch_cram_file_new ;ipatch_cram_fixup_ebml_size ;ipatch_cram_get_ebml_enum ;ipatch_cram_get_ebml_id ;ipatch_cram_read_ebml_chunk ;ipatch_cram_read_varlength ;ipatch_cram_sf2_file_encode ;ipatch_cram_store_ebml_chunk ;ipatch_cram_varlength_decode ;ipatch_cram_varlength_encode ;ipatch_cram_varlength_min_size ;ipatch_cram_varlength_size ;ipatch_cram_write_ebml_chunk ;ipatch_cram_write_ebml_chunk_buf ipatch_create_converter ;ipatch_create_path ;ipatch_dls2_conn_dest_type_get_type ipatch_dls2_conn_duplicate ipatch_dls2_conn_free ipatch_dls2_conn_get_type ipatch_dls2_conn_list_duplicate ipatch_dls2_conn_list_duplicate_fast ipatch_dls2_conn_list_free ipatch_dls2_conn_list_set ipatch_dls2_conn_list_unset ipatch_dls2_conn_new ;ipatch_dls2_conn_polarity_type_get_type ;ipatch_dls2_conn_src_type_get_type ;ipatch_dls2_conn_transform_masks_get_type ;ipatch_dls2_conn_transform_shifts_get_type ;ipatch_dls2_conn_transform_type_get_type ;ipatch_dls2_find_inst_ref ;ipatch_dls2_find_sample_ref ;ipatch_dls2_flags_get_type ipatch_dls2_get_info ipatch_dls2_get_type ipatch_dls2_info_bag_free ipatch_dls2_info_bag_new ipatch_dls2_info_duplicate ipatch_dls2_info_free ipatch_dls2_info_get ipatch_dls2_info_get_property ipatch_dls2_info_install_class_properties ipatch_dls2_info_is_defined ipatch_dls2_info_notify ipatch_dls2_info_peek ipatch_dls2_info_set ipatch_dls2_info_set_property ;ipatch_dls2_info_type_get_type ipatch_dls2_inst_compare ipatch_dls2_inst_conn_count ipatch_dls2_inst_first ;ipatch_dls2_inst_flags_get_type ipatch_dls2_inst_get_conns ipatch_dls2_inst_get_info ipatch_dls2_inst_get_midi_locale ipatch_dls2_inst_get_type ipatch_dls2_inst_new ipatch_dls2_inst_next ipatch_dls2_inst_set_conn ipatch_dls2_inst_set_info ipatch_dls2_inst_set_midi_locale ipatch_dls2_inst_unset_all_conns ipatch_dls2_inst_unset_conn ipatch_dls2_make_unique_name ipatch_dls2_new ;ipatch_dls2_ref_file ;ipatch_dls2_ref_region_references ipatch_dls2_region_channel_map_stereo ;ipatch_dls2_region_channel_type_get_type ;ipatch_dls2_region_clear_sample_override_flags ipatch_dls2_region_conn_count ipatch_dls2_region_first ;ipatch_dls2_region_flags_get_type ipatch_dls2_region_get_conns ipatch_dls2_region_get_info ;ipatch_dls2_region_get_sample_override ipatch_dls2_region_get_type ipatch_dls2_region_in_range ipatch_dls2_region_new ipatch_dls2_region_next ipatch_dls2_region_peek_sample ;ipatch_dls2_region_ref_sample ;ipatch_dls2_region_sample_override_get_type ipatch_dls2_region_set_conn ipatch_dls2_region_set_info ;ipatch_dls2_region_set_key_range ipatch_dls2_region_set_sample ;ipatch_dls2_region_set_sample_override ;ipatch_dls2_region_set_sample_override_flags ipatch_dls2_region_set_velocity_range ipatch_dls2_region_unset_all_conns ipatch_dls2_region_unset_conn ;ipatch_dls2_sample_find_store_ref ipatch_dls2_sample_first ;ipatch_dls2_sample_flags_get_type ;ipatch_dls2_sample_get_size ipatch_dls2_sample_get_type ipatch_dls2_sample_info_duplicate ipatch_dls2_sample_info_free ipatch_dls2_sample_info_get_property ipatch_dls2_sample_info_install_class_properties ipatch_dls2_sample_info_new ipatch_dls2_sample_info_set_property ipatch_dls2_sample_new ipatch_dls2_sample_next ipatch_dls2_sample_peek_data ;ipatch_dls2_sample_ref_data ipatch_dls2_sample_set_blank ipatch_dls2_sample_set_data ipatch_dls2_set_file ipatch_dls2_set_info ipatch_dls_file_get_type ipatch_dls_file_new ;ipatch_dls_load_connection ;ipatch_dls_load_dlid ;ipatch_dls_load_info ;ipatch_dls_load_pool_table ;ipatch_dls_load_region_header ;ipatch_dls_load_sample_format ;ipatch_dls_load_sample_info ;ipatch_dls_load_wave_link ;ipatch_dls_reader_error_get_type ipatch_dls_reader_error_quark ;ipatch_dls_reader_fixup ipatch_dls_reader_get_type ipatch_dls_reader_load ;ipatch_dls_reader_load_art_list ;ipatch_dls_reader_load_inst_list ;ipatch_dls_reader_load_level_0 ;ipatch_dls_reader_load_region_list ;ipatch_dls_reader_load_wave_pool ipatch_dls_reader_new ;ipatch_dls_reader_set_file ;ipatch_dls_reader_set_pool_table ;ipatch_dls_reader_start ipatch_dls_writer_get_type ipatch_dls_writer_new ipatch_dls_writer_save ;ipatch_dls_writer_set_file ipatch_dls_writer_set_patch ipatch_dump_object ;ipatch_error_get_type ipatch_error_quark ipatch_file_assign_fd ipatch_file_buf_commit ;ipatch_file_buf_free ;ipatch_file_buf_init ipatch_file_buf_load ipatch_file_buf_memset ipatch_file_buf_read ipatch_file_buf_read_s16 ipatch_file_buf_read_s32 ipatch_file_buf_read_s64 ipatch_file_buf_read_s8 ipatch_file_buf_read_u16 ipatch_file_buf_read_u32 ipatch_file_buf_read_u64 ipatch_file_buf_read_u8 ipatch_file_buf_seek ipatch_file_buf_write ipatch_file_buf_write_s16 ipatch_file_buf_write_s32 ipatch_file_buf_write_s64 ipatch_file_buf_write_s8 ipatch_file_buf_write_u16 ipatch_file_buf_write_u32 ipatch_file_buf_write_u64 ipatch_file_buf_write_u8 ipatch_file_close ;ipatch_file_convert_type ipatch_file_default_close_method ipatch_file_default_get_size_method ipatch_file_default_getfd_method ipatch_file_default_open_method ipatch_file_default_read_method ipatch_file_default_seek_method ipatch_file_default_write_method ipatch_file_get_fd ipatch_file_get_iofuncs ipatch_file_get_name ipatch_file_get_position ipatch_file_get_size ipatch_file_get_type ipatch_file_identify ipatch_file_identify_open ;ipatch_file_is_open ipatch_file_new ipatch_file_open ;ipatch_file_open_with_fd ;ipatch_file_open_with_io_channel ipatch_file_read ipatch_file_read_eof ipatch_file_read_s16 ipatch_file_read_s32 ipatch_file_read_s64 ipatch_file_read_s8 ipatch_file_read_u16 ipatch_file_read_u32 ipatch_file_read_u64 ipatch_file_read_u8 ;ipatch_file_ref_io_channel ipatch_file_seek ipatch_file_seek_eof ipatch_file_set_big_endian ipatch_file_set_iofuncs ipatch_file_set_iofuncs_null ipatch_file_set_iofuncs_static ipatch_file_set_little_endian ipatch_file_set_name ipatch_file_update_position ipatch_file_write ipatch_file_write_s16 ipatch_file_write_s32 ipatch_file_write_s64 ipatch_file_write_s8 ipatch_file_write_u16 ipatch_file_write_u32 ipatch_file_write_u64 ipatch_file_write_u8 ipatch_find_converter ;ipatch_find_paste_handler ipatch_glist_unref_free ipatch_gerror_message ;ipatch_get_conversion_dest_types ;ipatch_get_conversion_src_types ;ipatch_get_conversion_table ;ipatch_get_paste_dest_types ;ipatch_get_paste_src_types ;ipatch_gig_control_type_get_type ipatch_gig_dimension_first ipatch_gig_dimension_get_type ipatch_gig_dimension_new ipatch_gig_dimension_next ;ipatch_gig_dimension_type_get_type ipatch_gig_effects_init ipatch_gig_effects_to_gen_array ipatch_gig_file_get_type ipatch_gig_file_new ;ipatch_gig_filter_type_get_type ipatch_gig_get_type ipatch_gig_inst_first ipatch_gig_inst_get_type ipatch_gig_inst_new ipatch_gig_inst_next ;ipatch_gig_load_dimension_info ;ipatch_gig_load_dimension_names ;ipatch_gig_load_group_names ;ipatch_gig_load_region_header ;ipatch_gig_load_sample_info ipatch_gig_new ipatch_gig_parse_effects ;ipatch_gig_reader_load_inst_lart ;ipatch_gig_reader_load_region_list ;ipatch_gig_reader_load_sub_regions ipatch_gig_region_first ;ipatch_gig_region_flags_get_type ipatch_gig_region_get_type ipatch_gig_region_new ipatch_gig_region_new_dimension ipatch_gig_region_next ipatch_gig_region_remove_dimension ;ipatch_gig_region_set_key_range ipatch_gig_region_set_velocity_range ipatch_gig_sample_first ipatch_gig_sample_get_type ipatch_gig_sample_new ipatch_gig_sample_next ipatch_gig_store_effects ipatch_gig_sub_region_first ;ipatch_gig_sub_region_flags_get_type ipatch_gig_sub_region_get_type ipatch_gig_sub_region_new ipatch_gig_sub_region_next ;ipatch_gig_sub_region_ref_sample ipatch_gig_sub_region_set_sample ;ipatch_gig_sub_region_set_sample_info_override ipatch_gig_to_sf2_timecents ipatch_gig_volsust_to_sf2_centibels ;ipatch_iface_find_property ;ipatch_iface_get ;ipatch_iface_get_property ;ipatch_iface_get_valist ;ipatch_iface_install_property ;ipatch_iface_install_property_handlers ;ipatch_iface_list_properties ;ipatch_iface_set ;ipatch_iface_set_property ;ipatch_iface_set_valist ipatch_init ipatch_item_changed ;ipatch_item_clear_flags ipatch_item_copy ;ipatch_item_describe ipatch_item_duplicate ipatch_item_first ;ipatch_item_flags_get_type ipatch_item_get_atomic ipatch_item_get_base ipatch_item_get_flags ipatch_item_get_type ipatch_item_next ;ipatch_item_peek_ancestor_type ipatch_item_peek_ancestor_by_type ipatch_item_peek_base ipatch_item_peek_parent ipatch_item_prop_connect ipatch_item_prop_disconnect ipatch_item_prop_notify ipatch_item_prop_notify_by_name ;ipatch_item_pspec_title DATA ; Getter function returning title property GParamSpec as a convenience to derived type. ipatch_item_get_pspec_title ;ipatch_item_ref_ancestor_type ;ipatch_item_ref_base ;ipatch_item_ref_parent ipatch_item_remove ipatch_item_set_atomic ipatch_item_set_flags ipatch_item_get_parent ipatch_item_set_parent ;ipatch_item_set_prop_notify_hook ipatch_item_unparent ipatch_iter_GList_count ipatch_iter_GList_first ipatch_iter_GList_get ipatch_iter_GList_index ipatch_iter_GList_init ipatch_iter_GList_insert ipatch_iter_GList_last ;ipatch_iter_GList_methods DATA ipatch_iter_GList_next ipatch_iter_GList_remove ipatch_iter_GSList_count ipatch_iter_GSList_first ipatch_iter_GSList_get ipatch_iter_GSList_index ipatch_iter_GSList_init ipatch_iter_GSList_insert ipatch_iter_GSList_last ;ipatch_iter_GSList_methods DATA ipatch_iter_GSList_next ipatch_iter_GSList_remove ipatch_iter_alloc ipatch_iter_array_count ipatch_iter_array_first ipatch_iter_array_get ipatch_iter_array_index ipatch_iter_array_init ipatch_iter_array_insert ipatch_iter_array_last ;ipatch_iter_array_methods DATA ipatch_iter_array_next ipatch_iter_array_remove ipatch_iter_duplicate ipatch_iter_free ipatch_iter_get_type ipatch_list_duplicate ipatch_list_get_type ipatch_list_init_iter ipatch_list_new ipatch_lookup_converter_info ;ipatch_marshal_ENUM__FLAGS ipatch_md5_final ipatch_md5_init ipatch_md5_update ipatch_param_find_property ipatch_param_get ipatch_param_get_property ipatch_param_get_valist ipatch_param_install_property ipatch_param_list_properties ipatch_param_set ipatch_param_set_property ipatch_param_set_valist ipatch_param_spec_range ipatch_param_spec_range_get_type ipatch_is_paste_possible ;ipatch_paste_choice_flags_get_type ;ipatch_paste_choice_get_type ;ipatch_paste_default_handler ;ipatch_paste_execute ;ipatch_paste_find_flags_get_type ipatch_paste_get_add_list ;ipatch_paste_get_choice ipatch_paste_get_type ;ipatch_paste_is_possible ipatch_paste_new ipatch_paste_objects ;ipatch_paste_set_objects ipatch_paste_finish ipatch_range_copy ipatch_range_free ipatch_range_get_type ipatch_range_new ;ipatch_register_converter ipatch_register_paste_handler ;ipatch_riff_chunk_type_get_type ipatch_riff_close_chunk ipatch_riff_get_chunk ipatch_riff_get_chunk_array ipatch_riff_get_chunk_level ipatch_riff_get_error ;ipatch_riff_get_file ipatch_riff_get_position ipatch_riff_get_total_size ipatch_riff_message_detail ;ipatch_riff_mode_get_type ;ipatch_riff_parser_error_get_type ;ipatch_riff_parser_error_quark ;ipatch_riff_parser_get_type ;ipatch_riff_parser_new ipatch_riff_new ipatch_riff_pop_state ipatch_riff_push_state ipatch_riff_read_chunk ipatch_riff_read_chunk_verify ;ipatch_riff_set_file ipatch_riff_skip_chunks ipatch_riff_start_read ipatch_riff_start_read_chunk ;ipatch_riff_status_get_type ipatch_riff_write_chunk ;ipatch_sample_channel_get_type ;ipatch_sample_channel_route_get_type ;ipatch_sample_data_find_flags_get_type ;ipatch_sample_data_find_store_ref ;ipatch_sample_data_first ipatch_sample_data_get_cache_sample ipatch_set_sample_store_swap_file_name ipatch_base_save_to_filename ipatch_compact_sample_store_swap ipatch_set_sample_store_swap_max_memory ipatch_get_sample_store_swap_unused_size ;ipatch_sample_data_get_list ;ipatch_sample_data_get_primary_channels ;ipatch_sample_data_get_primary_format ;ipatch_sample_data_get_primary_frame_size ipatch_sample_data_get_size ipatch_sample_data_get_type ipatch_sample_data_get_native_format ipatch_sample_data_new ;ipatch_sample_data_next ;ipatch_sample_data_ref_blank ;ipatch_sample_data_ref_primary_store ;ipatch_sample_data_set_size ;ipatch_sample_endian_get_type ;ipatch_sample_file_get_save_types ;ipatch_sample_file_get_type ;ipatch_sample_find_flags_get_type ;ipatch_sample_find_source ipatch_sample_format_verify ipatch_sample_get_format ipatch_sample_get_type ipatch_sample_get_loop_types ipatch_sample_handle_close ipatch_sample_handle_get_max_frames ipatch_sample_handle_open ipatch_sample_handle_read ipatch_sample_get_sample_data ipatch_sample_install_property ipatch_sample_install_property_readonly ;ipatch_sample_loop_type_get_type ipatch_sample_new_property_param_spec ;ipatch_sample_new_transform_func_array ipatch_sample_data_open_cache_sample ;ipatch_sample_sign_get_type ;ipatch_sample_source_close ;ipatch_sample_source_get_format ;ipatch_sample_source_get_frame_size ;ipatch_sample_source_get_native_format ;ipatch_sample_source_get_transform ;ipatch_sample_source_read ;ipatch_sample_source_set_format ;ipatch_sample_speed_get_type ipatch_sample_cache_clean ipatch_sample_save_to_file ;ipatch_sample_store_FALSE ;ipatch_sample_store_RAM_get_location ;ipatch_sample_store_RAM_get_type ;ipatch_sample_store_RAM_new ;ipatch_sample_store_RAM_set_location ;ipatch_sample_store_ROM_get_location ;ipatch_sample_store_ROM_get_type ;ipatch_sample_store_ROM_new ;ipatch_sample_store_ROM_set_location ;ipatch_sample_store_TRUE ;ipatch_sample_store_activate ;ipatch_sample_store_alloc ;ipatch_sample_store_copy ipatch_sample_store_cache_get_type ;ipatch_sample_store_duplicate ;ipatch_sample_store_file_get_location ipatch_sample_store_cache_open ipatch_sample_store_cache_close ipatch_sample_store_file_get_type ipatch_sample_store_file_new ;ipatch_sample_store_file_peek_file ;ipatch_sample_store_file_ref_file ;ipatch_sample_store_file_set_file ;ipatch_sample_store_file_set_location ipatch_sample_store_first ;ipatch_sample_store_flags_get_type ;ipatch_sample_store_get_active ;ipatch_sample_store_get_format ;ipatch_sample_store_get_frame_size ;ipatch_sample_store_get_size ;ipatch_sample_store_get_speed ipatch_sample_store_get_type ;ipatch_sample_store_is_readable ;ipatch_sample_store_is_writable ipatch_sample_store_next ;ipatch_sample_store_read ;ipatch_sample_store_set_format ipatch_sample_store_swap_get_type ipatch_sample_store_swap_new ;ipatch_sample_store_transform_init ;ipatch_sample_store_transform_init_size ;ipatch_sample_store_transform_new ;ipatch_sample_store_transform_new_size ;ipatch_sample_store_transform_read ;ipatch_sample_store_transform_write ;ipatch_sample_store_write ipatch_sample_transform_alloc ipatch_sample_transform_alloc_size ipatch_sample_transform_convert ipatch_sample_transform_free_buffers ipatch_sample_transform_get_buffers ipatch_sample_transform_get_frame_sizes ipatch_sample_transform_get_max_frames ;ipatch_sample_transform_get_type ipatch_sample_transform_new ;ipatch_sample_transform_set_buffers ipatch_sample_transform_set_formats ;ipatch_sample_width_get_type ;ipatch_sample_width_sizes DATA ipatch_set_application_name ipatch_sf2_file_get_sample_pos ipatch_sf2_file_get_sample_size ipatch_sf2_file_get_type ipatch_sf2_file_new ipatch_sf2_file_set_sample_pos ipatch_sf2_file_set_sample_size ;ipatch_sf2_find_inst_ref ;ipatch_sf2_find_preset_ref ;ipatch_sf2_find_sample_ref ipatch_sf2_free_info_array ;ipatch_sf2_gen_abs_array DATA ;ipatch_sf2_gen_abs_valid_mask DATA ;ipatch_sf2_gen_add_mask DATA ipatch_sf2_gen_amount_to_value ipatch_sf2_gen_array_duplicate ipatch_sf2_gen_array_free ipatch_sf2_gen_array_get_type ipatch_sf2_gen_array_init ipatch_sf2_gen_array_new ipatch_sf2_gen_array_offset ;ipatch_sf2_gen_info DATA ; Getter function returning ipatch_sf2_gen_info table. ipatch_sf2_get_gen_info ipatch_sf2_gen_item_get_type ipatch_sf2_gen_item_copy_all ;ipatch_sf2_gen_ofs_array DATA ;ipatch_sf2_gen_ofs_valid_mask DATA ;ipatch_sf2_gen_sample_modes_get_type ;ipatch_sf2_gen_type_get_type ;ipatch_sf2_genid_default_value ;ipatch_sf2_genid_offset ;ipatch_sf2_genid_unit_str ipatch_sf2_get_info ipatch_sf2_get_info_array ipatch_sf2_get_info_max_size ipatch_sf2_get_type ipatch_sf2_info_id_is_valid ;ipatch_sf2_info_type_get_type ipatch_sf2_inst_first ipatch_sf2_inst_get_name ipatch_sf2_inst_get_type ;ipatch_sf2_inst_has_global_zone ipatch_sf2_inst_new ipatch_sf2_inst_new_zone ipatch_sf2_inst_next ipatch_sf2_inst_set_name ipatch_sf2_izone_first ipatch_sf2_izone_get_type ipatch_sf2_izone_new ipatch_sf2_izone_next ;ipatch_sf2_izone_ref_sample ipatch_sf2_izone_set_sample ;ipatch_sf2_load_bag ;ipatch_sf2_load_gen ;ipatch_sf2_load_ihdr ;ipatch_sf2_load_mod ;ipatch_sf2_load_phdr ;ipatch_sf2_load_shdr ipatch_sf2_make_unique_name ;ipatch_sf2_mod_control_get_type ;ipatch_sf2_mod_control_palette_get_type ;ipatch_sf2_mod_direction_get_type ipatch_sf2_mod_duplicate ;ipatch_sf2_mod_field_masks_get_type ;ipatch_sf2_mod_field_shifts_get_type ipatch_sf2_mod_free ipatch_sf2_mod_get_type ipatch_sf2_mod_list_duplicate ipatch_sf2_mod_list_get_type ipatch_sf2_mod_list_free ipatch_sf2_mod_list_change ipatch_sf2_mod_list_get_default ipatch_sf2_mod_list_offset ipatch_sf2_mod_list_override ipatch_sf2_mod_list_insert ipatch_sf2_mod_list_remove ipatch_sf2_mod_new ipatch_sf2_mod_item_get_type ;ipatch_sf2_mod_polarity_get_type ;ipatch_sf2_mod_transform_get_type ;ipatch_sf2_mod_type_get_type ipatch_sf2_new ipatch_sf2_preset_compare ipatch_sf2_preset_first ipatch_sf2_preset_get_midi_locale ipatch_sf2_preset_get_name ipatch_sf2_preset_get_type ;ipatch_sf2_preset_has_global_zone ipatch_sf2_preset_new ipatch_sf2_preset_new_zone ipatch_sf2_preset_next ipatch_sf2_preset_set_midi_locale ipatch_sf2_preset_set_name ipatch_sf2_pzone_first ipatch_sf2_pzone_get_type ipatch_sf2_pzone_new ipatch_sf2_pzone_next ;ipatch_sf2_pzone_ref_inst ipatch_sf2_pzone_set_inst ipatch_sf2_reader_get_type ipatch_sf2_reader_load ipatch_sf2_reader_new ;ipatch_sf2_reader_set_file ;ipatch_sf2_ref_file ;ipatch_sf2_ref_zone_references ;ipatch_sf2_sample_find_store_ref ipatch_sf2_sample_first ;ipatch_sf2_sample_flags_get_type ipatch_sf2_sample_get_name ;ipatch_sf2_sample_get_size ipatch_sf2_sample_get_type ipatch_sf2_sample_new ipatch_sf2_sample_next ipatch_sf2_sample_peek_data ipatch_sf2_sample_peek_linked ;ipatch_sf2_sample_ref_data ;ipatch_sf2_sample_ref_linked ipatch_sf2_sample_set_blank ipatch_sf2_sample_set_data ipatch_sf2_sample_set_linked ipatch_sf2_sample_set_name ipatch_sf2_set_file ipatch_sf2_set_info ;ipatch_sf2_units_clamp ;ipatch_sf2_units_range_intersect ;ipatch_sf2_units_sfont_to_user ;ipatch_sf2_units_sfont_to_user_str ;ipatch_sf2_units_user_to_sfont ipatch_sf2_voice_cache_add_voice ipatch_sf2_voice_cache_get_type ipatch_sf2_voice_cache_new ipatch_sf2_voice_cache_optimize ipatch_sf2_voice_cache_select ipatch_sf2_voice_cache_set_voice_range ipatch_sf2_voice_cache_set_default_mods ipatch_sf2_voice_cache_sample_data ipatch_sf2_voice_cache_update ;ipatch_sf2_voice_convert_sample_data ;ipatch_sf2_voice_sel_type_get_type ipatch_sf2_write_bag ipatch_sf2_write_gen ipatch_sf2_write_ihdr ipatch_sf2_write_mod ipatch_sf2_write_phdr ipatch_sf2_write_shdr ipatch_sf2_writer_get_type ipatch_sf2_writer_new ipatch_sf2_writer_save ;ipatch_sf2_writer_set_file ipatch_sf2_writer_set_patch ;ipatch_sf2_zone_class_install_gen_properties ;ipatch_sf2_zone_copy_gen_array ;ipatch_sf2_zone_fill_gen_array ipatch_sf2_zone_first ;ipatch_sf2_zone_gen_count ;ipatch_sf2_zone_genid_get_prop_name ;ipatch_sf2_zone_genid_get_pspec ;ipatch_sf2_zone_get_gen ;ipatch_sf2_zone_get_gen_property ;ipatch_sf2_zone_get_mods ipatch_sf2_zone_get_type ;ipatch_sf2_zone_in_range ;ipatch_sf2_zone_insert_mod ;ipatch_sf2_zone_mod_count ipatch_sf2_zone_next ;ipatch_sf2_zone_peek_item ;ipatch_sf2_zone_ref_item ;ipatch_sf2_zone_remove_mod ;ipatch_sf2_zone_set_gen ;ipatch_sf2_zone_set_gen_property ;ipatch_sf2_zone_set_item ;ipatch_sf2_zone_set_key_range ;ipatch_sf2_zone_set_mod ;ipatch_sf2_zone_set_velocity_range ;ipatch_sf2_zone_unset_gen ipatch_strconcat_num ipatch_sli_get_type ;ipatch_sli_inst_cat_strings ;getter fonction that returns pointer on ipatch_sli_inst_cat_strings table ipatch_sli_inst_get_cat_strings ipatch_sli_sample_get_type ;ipatch_sli_inst_cat_map ;getter fonction that returns pointer on ipatch_sli_inst_cat_map table ipatch_sli_inst_get_cat_map ipatch_sli_inst_get_category_as_path ipatch_sli_inst_get_type ipatch_sli_zone_get_type ipatch_type_find_property ipatch_type_find_types_with_property ipatch_type_get ipatch_type_get_dynamic_func ipatch_type_get_property ipatch_type_get_valist ipatch_type_install_property ipatch_type_list_properties ipatch_type_object_get ipatch_type_object_get_property ipatch_type_object_get_valist ;ipatch_type_register_func ipatch_type_set ipatch_type_set_property ipatch_type_set_valist ;ipatch_unit_centibel_to_decibel ;ipatch_unit_centibel_to_dls_gain ipatch_unit_cents_to_hertz ipatch_unit_class_lookup_map ipatch_unit_class_register_map ;ipatch_unit_class_type_get_type ipatch_unit_conversion_lookup ipatch_unit_conversion_register ipatch_unit_convert ;ipatch_unit_decibel_to_centibel ;ipatch_unit_decibel_to_dls_gain ipatch_unit_dls_abs_pitch_to_hertz ipatch_unit_dls_abs_pitch_to_sf2_abs_pitch ipatch_unit_dls_abs_time_to_seconds ipatch_unit_dls_abs_time_to_sf2_abs_time ipatch_unit_dls_class_convert ;ipatch_unit_dls_gain_to_centibel ;ipatch_unit_dls_gain_to_decibel ;ipatch_unit_flags_get_type ipatch_unit_hertz_to_cents ipatch_unit_hertz_to_dls_abs_pitch ipatch_unit_hertz_to_sf2_abs_pitch ;ipatch_unit_info_alloc ipatch_unit_info_free ;ipatch_unit_lookup_by_id ipatch_unit_lookup_by_name ipatch_unit_percent_to_tenth_percent ipatch_unit_register ipatch_unit_seconds_to_dls_abs_time ipatch_unit_seconds_to_sf2_abs_time ipatch_unit_sf2_abs_pitch_to_dls_abs_pitch ipatch_unit_sf2_abs_pitch_to_hertz ipatch_unit_sf2_abs_time_to_dls_abs_time ipatch_unit_sf2_abs_time_to_seconds ipatch_unit_tenth_percent_to_percent ;ipatch_unit_type_get_type ipatch_unit_user_class_convert ipatch_value_get_range ipatch_value_set_range ipatch_value_set_static_range ipatch_version ipatch_vbank_get_type ipatch_vbank_new ipatch_virtual_container_get_type ipatch_virtual_dls2_melodic_get_type ipatch_virtual_dls2_percussion_get_type ipatch_virtual_dls2_samples_get_type ipatch_virtual_gig_melodic_get_type ipatch_virtual_gig_percussion_get_type ipatch_virtual_gig_samples_get_type ipatch_virtual_sf2_inst_get_type ipatch_virtual_sf2_melodic_get_type ipatch_virtual_sf2_percussion_get_type ipatch_virtual_sf2_rom_get_type ipatch_virtual_sf2_samples_get_type ipatch_xml_copy ipatch_xml_decode_property ipatch_xml_decode_object ipatch_xml_destroy ipatch_xml_encode_object ipatch_xml_get_name ipatch_xml_get_attribute ipatch_xml_new_node ipatch_xml_to_str ipatch_xml_test_attribute ipatch_xml_test_name ipatch_xml_load_from_file ipatch_simple_paste ipatch_snd_file_get_type libinstpatch-1.1.6/libinstpatch/libinstpatch.h.in000066400000000000000000000106401400263525300221320ustar00rootroot00000000000000/* * libinstpatch.h - Main public header file for libInstPatch * * libInstPatch - MIDI instrument patch library * Copyright (C) 1999-2007 Josh Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __LIB_INST_PATCH_H__ #define __LIB_INST_PATCH_H__ #include G_BEGIN_DECLS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include G_END_DECLS #endif libinstpatch-1.1.6/libinstpatch/marshals.list000066400000000000000000000023371400263525300214030ustar00rootroot00000000000000# see glib-genmarshal(1) for a detailed description of the file format, # possible parameter types are: # VOID indicates no return type, or no extra # parameters. if VOID is used as the parameter # list, no additional parameters may be present. # BOOLEAN for boolean types (gboolean) # CHAR for signed char types (gchar) # UCHAR for unsigned char types (guchar) # INT for signed integer types (gint) # UINT for unsigned integer types (guint) # LONG for signed long integer types (glong) # ULONG for unsigned long integer types (gulong) # ENUM for enumeration types (gint) # FLAGS for flag enumeration types (guint) # FLOAT for single-precision float types (gfloat) # DOUBLE for double-precision float types (gdouble) # STRING for string types (gchar*) # PARAM for GParamSpec or derived types (GParamSpec*) # BOXED for boxed (anonymous but reference counted) types (GBoxed*) # POINTER for anonymous pointer types (gpointer) # OBJECT for GObject or derived types (GObject*) # NONE deprecated alias for VOID # BOOL deprecated alias for BOOLEAN ENUM:FLAGS libinstpatch-1.1.6/libinstpatch/md5.c000066400000000000000000000205501400263525300175220ustar00rootroot00000000000000/* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * IpatchMD5 structure, pass it to ipatch_md5_init, call ipatch_md5_update as * needed on buffers full of bytes, and then call ipatch_md5_final, which * will fill a supplied 16-byte array with the digest. * * Changed so as no longer to depend on Colin Plumb's `usual.h' header * definitions; now uses stuff from dpkg's config.h. * - Ian Jackson . * Still in the public domain. * * Josh Coalson: made some changes to integrate with libFLAC. * Still in the public domain. * * Josh Green: made some changes to integrate with libInstPatch. * Still in the public domain. */ /** * SECTION: md5 * @short_description: MD5 hashing functions * @see_also: * @stability: Stable */ #include /* for malloc() */ #include /* for memcpy() */ #include #include "md5.h" /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f,w,x,y,z,in,s) \ (w += f(x,y,z) + in, w = (w<>(32-s)) + x) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. ipatch_md5_update blocks * the data and converts bytes into longwords for this routine. */ static inline void MD5Transform(guint32 buf[4], guint32 const in[16]) { register guint32 a, b, c, d; a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } static inline void byteSwap(guint32 *buf, unsigned words) { guint8 *p = (guint8 *)buf; if(G_BYTE_ORDER != G_BIG_ENDIAN) { return; } do { *buf++ = (guint32)((unsigned)p[3] << 8 | p[2]) << 16 | ((unsigned)p[1] << 8 | p[0]); p += 4; } while(--words); } /** * ipatch_md5_init: (skip) * @ctx: MD5 context * * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ void ipatch_md5_init(IpatchMD5 *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; ctx->buf[2] = 0x98badcfe; ctx->buf[3] = 0x10325476; ctx->bytes[0] = 0; ctx->bytes[1] = 0; } /* * ipatch_md5_update: (skip) * @ctx: MD5 context * @buf: Buffer of data * @len: Length of data in @buf * * Update MD5 context to reflect the concatenation of another buffer full * of bytes. */ void ipatch_md5_update(IpatchMD5 *ctx, guint8 const *buf, unsigned len) { guint32 t; /* Update byte count */ t = ctx->bytes[0]; if((ctx->bytes[0] = t + len) < t) { ctx->bytes[1]++; /* Carry from low to high */ } t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ if(t > len) { memcpy((guint8 *)ctx->in + 64 - t, buf, len); return; } /* First chunk is an odd size */ memcpy((guint8 *)ctx->in + 64 - t, buf, t); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); buf += t; len -= t; /* Process data in 64-byte chunks */ while(len >= 64) { memcpy(ctx->in, buf, 64); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ memcpy(ctx->in, buf, len); } /** * ipatch_md5_final: (skip) * @ctx: MD5 context * @digest: Buffer to store 16 byte MD5 digest into * * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ void ipatch_md5_final(IpatchMD5 *ctx, guint8 digest[16]) { int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ guint8 *p = (guint8 *)ctx->in + count; /* Set the first char of padding to 0x80. There is always room. */ *p++ = 0x80; /* Bytes of padding needed to make 56 bytes (-8..55) */ count = 56 - 1 - count; if(count < 0) /* Padding forces an extra block */ { memset(p, 0, count + 8); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); p = (guint8 *)ctx->in; count = 56; } memset(p, 0, count); byteSwap(ctx->in, 14); /* Append length in bits and transform */ ctx->in[14] = ctx->bytes[0] << 3; ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; MD5Transform(ctx->buf, ctx->in); byteSwap(ctx->buf, 4); memcpy(digest, ctx->buf, 16); memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } libinstpatch-1.1.6/libinstpatch/md5.h000066400000000000000000000027521400263525300175330ustar00rootroot00000000000000/* * This is the header file for the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * IpatchMD5 structure, pass it to ipatch_md5_init, call ipatch_md5_update as * needed on buffers full of bytes, and then call ipatch_md5_final, which * will fill a supplied 16-byte array with the digest. * * Changed so as no longer to depend on Colin Plumb's `usual.h' * header definitions; now uses stuff from dpkg's config.h * - Ian Jackson . * Still in the public domain. * * Josh Coalson: made some changes to integrate with libFLAC. * Still in the public domain. * * Josh Green: made some changes to integrate with libInstPatch. * Still in the public domain. */ #ifndef __IPATCH__MD5_H__ #define __IPATCH__MD5_H__ #include /** * IpatchMD5: (skip) */ typedef struct { guint32 buf[4]; guint32 bytes[2]; guint32 in[16]; } IpatchMD5; void ipatch_md5_init(IpatchMD5 *ctx); void ipatch_md5_update(IpatchMD5 *ctx, guint8 const *buf, unsigned len); void ipatch_md5_final(IpatchMD5 *ctx, guint8 digest[16]); #endif /* !MD5_H */ libinstpatch-1.1.6/libinstpatch/misc.c000066400000000000000000001006441400263525300177730ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: misc * @short_description: Miscellaneous stuff * @see_also: * @stability: Stable */ #if defined(HAVE_CONFIG_H) #include "config.h" #endif #include #include #include #include #include /* for mkdir */ #include #include #include "libinstpatch.h" #include "ipatch_priv.h" #include "i18n.h" /* private initializers in other source files */ void _ipatch_sf2_gen_init(void); /* IpatchSF2Gen.c */ void _ipatch_param_init(void); /* IpatchParam.c */ void _ipatch_type_prop_init(void); /* IpatchTypeProp.c */ void _ipatch_util_init(void); /* util.c */ void _ipatch_unit_init(void); /* unit.c */ void _ipatch_xml_object_init(void); /* IpatchXmlObject.c */ void _ipatch_range_init(void); /* IpatchRange.c */ void _ipatch_convert_SF2_init(void); void _ipatch_convert_gig_init(void); void _ipatch_convert_DLS2_init(void); void _ipatch_convert_SLI_init(void); void _ipatch_sf2_voice_cache_init_DLS(void); void _ipatch_sf2_voice_cache_init_SF2(void); void _ipatch_sf2_voice_cache_init_SLI(void); void _ipatch_sf2_voice_cache_init_gig(void); void _ipatch_sf2_voice_cache_init_VBank(void); void _ipatch_container_notify_init(void); void _ipatch_DLS2_infos_init(void); void _ipatch_DLS2_sample_init(void); void _ipatch_file_init(void); void _ipatch_item_init(void); void _ipatch_sample_data_init(void); void _ipatch_sample_store_swap_recover_init(void); void _ipatch_converter_init(void); void _ipatch_sample_transform_init(); void _ipatch_sf2_mod_list_init(); void _ipatch_paste_init(void); /* private free functions in other source files */ void _ipatch_param_deinit(void); void _ipatch_type_prop_deinit(void); void _ipatch_unit_deinit(void); void _ipatch_xml_object_deinit(void); void _ipatch_util_deinit(void); void _ipatch_sf2_gen_deinit(void); void _ipatch_container_notify_deinit(void); void _ipatch_DLS2_infos_deinit(void); void _ipatch_DLS2_sample_deinit(void); void _ipatch_file_deinit(void); void _ipatch_item_deinit(void); void _ipatch_sample_data_deinit(void); void _ipatch_sample_store_swap_recover_deinit(void); void _ipatch_converter_deinit(void); void _ipatch_sample_transform_deinit(void); void _ipatch_sf2_mod_list_deinit(void); void _ipatch_paste_deinit(void); static gboolean ipatch_strv_xml_encode(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err); static gboolean ipatch_strv_xml_decode(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err); static void virtual_parent_dls2_inst(GType type, GParamSpec *spec, GValue *value, GObject *object); static void virtual_parent_gig_inst(GType type, GParamSpec *spec, GValue *value, GObject *object); static void virtual_parent_sf2_preset(GType type, GParamSpec *spec, GValue *value, GObject *object); static void virtual_parent_sf2_sample(GType type, GParamSpec *spec, GValue *value, GObject *object); static void conform_percussion(GObject *object); static void conform_melodic(GObject *object); static void dump_recursive(GObject *object, char *indent, FILE *file); static void dump_object_info(GObject *object, char *indent, FILE *file); typedef struct { char *type_name; char *name; char *blurb; int category; } TypePropInit; /* info to initialize type properties */ static TypePropInit type_props[] = { { "IpatchSampleStoreSndFile", N_("Sample file"), NULL, IPATCH_CATEGORY_SAMPLE }, { "IpatchDLS2", N_("DLS"), N_("Down Loadable Sounds"), IPATCH_CATEGORY_BASE }, { "IpatchDLS2Inst", N_("Instrument"), N_("DLS Instrument"), IPATCH_CATEGORY_PROGRAM }, { "IpatchDLS2Region", N_("Region"), N_("DLS Region"), IPATCH_CATEGORY_SAMPLE_REF }, { "IpatchDLS2Sample", N_("Sample"), N_("DLS Sample"), IPATCH_CATEGORY_SAMPLE }, { "IpatchGig", N_("GigaSampler"), NULL, IPATCH_CATEGORY_BASE }, { "IpatchGigDimension", N_("Dimension"), N_("GigaSampler Dimension"), IPATCH_CATEGORY_NONE }, { "IpatchGigInst", N_("Instrument"), N_("GigaSampler Instrument"), IPATCH_CATEGORY_PROGRAM }, { "IpatchGigRegion", N_("Region"), N_("GigaSampler Region"), IPATCH_CATEGORY_NONE }, { "IpatchGigSample", N_("Sample"), N_("GigaSampler Sample"), IPATCH_CATEGORY_SAMPLE }, { "IpatchGigSubRegion", N_("Sub Region"), N_("GigaSampler Sub Region"), IPATCH_CATEGORY_SAMPLE_REF }, { "IpatchSF2", N_("SoundFont"), NULL, IPATCH_CATEGORY_BASE }, { "IpatchSF2Inst", N_("Instrument"), N_("SoundFont Instrument"), IPATCH_CATEGORY_INSTRUMENT }, { "IpatchSF2IZone", N_("Zone"), N_("SoundFont Instrument Zone"), IPATCH_CATEGORY_SAMPLE_REF }, { "IpatchSF2Preset", N_("Preset"), N_("SoundFont Preset"), IPATCH_CATEGORY_PROGRAM }, { "IpatchSF2PZone", N_("Zone"), N_("SoundFont Preset Zone"), IPATCH_CATEGORY_INSTRUMENT_REF }, { "IpatchSF2Sample", N_("Sample"), N_("SoundFont Sample"), IPATCH_CATEGORY_SAMPLE }, { "IpatchSLI", N_("Spectralis"), NULL, IPATCH_CATEGORY_BASE }, { "IpatchSLIInst", N_("Instrument"), N_("Spectralis Instrument"), IPATCH_CATEGORY_INSTRUMENT }, { "IpatchSLIZone", N_("Zone"), N_("Spectralis Instrument Zone"), IPATCH_CATEGORY_SAMPLE_REF }, { "IpatchSLISample", N_("Sample"), N_("Spectralis Sample"), IPATCH_CATEGORY_SAMPLE }, { "IpatchVBank", N_("VBank"), N_("Virtual Bank"), IPATCH_CATEGORY_BASE }, { "IpatchVBankInst", N_("Instrument"), N_("VBank Instrument"), IPATCH_CATEGORY_PROGRAM }, { "IpatchVBankRegion", N_("Region"), N_("VBank Region"), IPATCH_CATEGORY_INSTRUMENT_REF } }; /* name of application using libInstPatch (for saving to files) */ char *ipatch_application_name = NULL; G_LOCK_DEFINE_STATIC(lock_init); static int init_counter = 0; /*----------------------------------------------------------------------------- Initialization / deinitialization of libinstpatch library. Any application should call ipatch_init() once before any other libinstpatch functions. When the application is complete it must call ipatch_close(). For multi task application it is best that only one task be responsible of initialization/closing. Typically, the main task of the application should call ipatch_init() before creating other tasks, then when the application is complete, the main task should call ipatch_close() after the other tasks are completed. If the application make use of multiple libraries each dependent of libinstpatch, for each call to ipatch_init() these libraries should call ipatch_close(). -----------------------------------------------------------------------------*/ /** * ipatch_init: * * Initialize libInstPatch library. Should be called before any other * libInstPatch related functions. */ void ipatch_init(void) { TypePropInit *prop_info; GType type; int i; /* do nothing if the library is already initialized */ G_LOCK(lock_init); init_counter++; if(init_counter > 1) { /* library already initialized */ G_UNLOCK(lock_init); return; } g_type_init(); if(!g_thread_supported()) { g_thread_init(NULL); } /* bind the gettext domain */ #if defined(ENABLE_NLS) bindtextdomain(PACKAGE, LOCALEDIR); #endif /* Must be done before other types since they may be dependent */ /* Initialize 'GParamSpec extended properties' system */ _ipatch_param_init(); /* Initialize the 'GObject style properties' system for GTypes */ _ipatch_type_prop_init(); /* Initialize 'unit conversion' system */ _ipatch_unit_init(); /* Initialize object's properties 'encoding/decoding XML handlers' system */ _ipatch_xml_object_init(); /* Initialize GValue constant values */ _ipatch_util_init(); /* Initialize 'SoundFont generators' subsystem */ _ipatch_sf2_gen_init(); /*------------------------------------------------------------------------ Initialize object subsystem (list, hash) before objects type. These list or hash are specfific to the respective object. Initialization/free functions are in the respective object module file Here initialization function _xxx_init() are called. Respective function _xxx_deinit() are called in ipatch_deinit(). -------------------------------------------------------------------------*/ _ipatch_container_notify_init(); _ipatch_DLS2_infos_init(); _ipatch_DLS2_sample_init(); _ipatch_file_init(); _ipatch_item_init(); _ipatch_sample_data_init(); _ipatch_sample_store_swap_recover_init(); _ipatch_converter_init(); _ipatch_sample_transform_init(); _ipatch_sf2_mod_list_init(); _ipatch_paste_init(); /*------------------------------------------------------------------------- initialize interfaces type before objects --------------------------------------------------------------------------*/ /* initialize interfaces before objects */ ipatch_sample_get_type(); ipatch_sf2_gen_item_get_type(); ipatch_sf2_mod_item_get_type(); /* declares property types which other types may use */ g_type_class_ref(IPATCH_TYPE_SF2_VOICE_CACHE); g_type_class_ref(IPATCH_TYPE_BASE); g_type_class_ref(IPATCH_TYPE_CONTAINER); g_type_class_ref(IPATCH_TYPE_CONVERTER); g_type_class_ref(IPATCH_TYPE_DLS2); ipatch_dls2_conn_get_type(); g_type_class_ref(IPATCH_TYPE_DLS2_INST); g_type_class_ref(IPATCH_TYPE_DLS2_REGION); g_type_class_ref(IPATCH_TYPE_DLS2_SAMPLE); g_type_class_ref(IPATCH_TYPE_DLS_FILE); g_type_class_ref(IPATCH_TYPE_DLS_READER); g_type_class_ref(IPATCH_TYPE_DLS_WRITER); g_type_class_ref(IPATCH_TYPE_FILE); ipatch_file_handle_get_type(); g_type_class_ref(IPATCH_TYPE_GIG_FILE); g_type_class_ref(IPATCH_TYPE_GIG); g_type_class_ref(IPATCH_TYPE_GIG_DIMENSION); g_type_class_ref(IPATCH_TYPE_GIG_INST); g_type_class_ref(IPATCH_TYPE_GIG_REGION); g_type_class_ref(IPATCH_TYPE_GIG_SAMPLE); g_type_class_ref(IPATCH_TYPE_GIG_SUB_REGION); g_type_class_ref(IPATCH_TYPE_ITEM); ipatch_iter_get_type(); g_type_class_ref(IPATCH_TYPE_LIST); ipatch_param_spec_range_get_type(); g_type_class_ref(IPATCH_TYPE_PASTE); ipatch_range_get_type(); g_type_class_ref(IPATCH_TYPE_RIFF); g_type_class_ref(IPATCH_TYPE_SAMPLE_DATA); g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE); g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE_FILE); g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE_RAM); g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE_ROM); g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE_SND_FILE); g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE_SPLIT24); g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE_SWAP); g_type_class_ref(IPATCH_TYPE_SAMPLE_STORE_VIRTUAL); g_type_class_ref(IPATCH_TYPE_SF2_FILE); ipatch_sf2_gen_array_get_type(); g_type_class_ref(IPATCH_TYPE_SF2); g_type_class_ref(IPATCH_TYPE_SF2_INST); g_type_class_ref(IPATCH_TYPE_SF2_IZONE); g_type_class_ref(IPATCH_TYPE_SF2_READER); ipatch_sf2_mod_get_type(); ipatch_sf2_mod_list_get_type(); ipatch_sample_transform_get_type(); ipatch_sample_list_get_type(); ipatch_sample_list_item_get_type(); g_type_class_ref(IPATCH_TYPE_SF2_PRESET); g_type_class_ref(IPATCH_TYPE_SF2_PZONE); g_type_class_ref(IPATCH_TYPE_SF2_SAMPLE); g_type_class_ref(IPATCH_TYPE_SLI_FILE); g_type_class_ref(IPATCH_TYPE_SLI); g_type_class_ref(IPATCH_TYPE_SLI_INST); g_type_class_ref(IPATCH_TYPE_SLI_ZONE); g_type_class_ref(IPATCH_TYPE_SLI_SAMPLE); g_type_class_ref(IPATCH_TYPE_SLI_READER); g_type_class_ref(IPATCH_TYPE_VBANK); g_type_class_ref(IPATCH_TYPE_VBANK_INST); g_type_class_ref(IPATCH_TYPE_VBANK_REGION); g_type_class_ref(IPATCH_TYPE_SF2_WRITER); g_type_class_ref(IPATCH_TYPE_SF2_ZONE); g_type_class_ref(IPATCH_TYPE_SND_FILE); _ipatch_convert_SF2_init(); _ipatch_convert_gig_init(); _ipatch_convert_DLS2_init(); _ipatch_convert_SLI_init(); _ipatch_sf2_voice_cache_init_DLS(); _ipatch_sf2_voice_cache_init_SF2(); _ipatch_sf2_voice_cache_init_SLI(); _ipatch_sf2_voice_cache_init_gig(); _ipatch_sf2_voice_cache_init_VBank(); _ipatch_range_init(); /* Register XML encode/decode handlers */ /* GLib string array boxed type encode/decode */ ipatch_xml_register_handler(G_TYPE_STRV, NULL, ipatch_strv_xml_encode, ipatch_strv_xml_decode); /* set type properties */ for(i = 0; i < G_N_ELEMENTS(type_props); i++) { type = g_type_from_name(type_props[i].type_name); if(log_if_fail(type != 0)) { continue; } prop_info = &type_props[i]; if(prop_info->name) { ipatch_type_set(type, "name", prop_info->name, NULL); } if(prop_info->blurb) { ipatch_type_set(type, "blurb", prop_info->blurb, NULL); } if(prop_info->category != IPATCH_CATEGORY_NONE) { ipatch_type_set(type, "category", prop_info->category, NULL); } } /* link types */ ipatch_type_set(IPATCH_TYPE_DLS2_REGION, "link-type", IPATCH_TYPE_DLS2_SAMPLE, NULL); ipatch_type_set(IPATCH_TYPE_GIG_SUB_REGION, "link-type", IPATCH_TYPE_GIG_SAMPLE, NULL); ipatch_type_set(IPATCH_TYPE_SF2_PZONE, "link-type", IPATCH_TYPE_SF2_INST, NULL); ipatch_type_set(IPATCH_TYPE_SF2_IZONE, "link-type", IPATCH_TYPE_SF2_SAMPLE, NULL); ipatch_type_set(IPATCH_TYPE_SLI_ZONE, "link-type", IPATCH_TYPE_SLI_SAMPLE, NULL); ipatch_type_set(IPATCH_TYPE_VBANK_REGION, "link-type", IPATCH_TYPE_ITEM, NULL); /* virtual container parent type properties */ ipatch_type_set(IPATCH_TYPE_DLS2_SAMPLE, "virtual-parent-type", IPATCH_TYPE_VIRTUAL_DLS2_SAMPLES, NULL); ipatch_type_set(IPATCH_TYPE_GIG_SAMPLE, "virtual-parent-type", IPATCH_TYPE_VIRTUAL_GIG_SAMPLES, NULL); ipatch_type_set(IPATCH_TYPE_SF2_INST, "virtual-parent-type", IPATCH_TYPE_VIRTUAL_SF2_INST, NULL); ipatch_type_set(IPATCH_TYPE_SLI_INST, "virtual-parent-type", IPATCH_TYPE_VIRTUAL_SLI_INST, NULL); ipatch_type_set(IPATCH_TYPE_SLI_SAMPLE, "virtual-parent-type", IPATCH_TYPE_VIRTUAL_SLI_SAMPLES, NULL); /* dynamic virtual container properties (determined by object instance) */ ipatch_type_set_dynamic_func(IPATCH_TYPE_DLS2_INST, "virtual-parent-type", virtual_parent_dls2_inst); ipatch_type_set_dynamic_func(IPATCH_TYPE_GIG_INST, "virtual-parent-type", virtual_parent_gig_inst); ipatch_type_set_dynamic_func(IPATCH_TYPE_SF2_PRESET, "virtual-parent-type", virtual_parent_sf2_preset); ipatch_type_set_dynamic_func(IPATCH_TYPE_SF2_SAMPLE, "virtual-parent-type", virtual_parent_sf2_sample); /* child object conform functions (for making a child object conform to a * specific virtual container) */ ipatch_type_set(IPATCH_TYPE_VIRTUAL_DLS2_PERCUSSION, "virtual-child-conform-func", conform_percussion, NULL); ipatch_type_set(IPATCH_TYPE_VIRTUAL_DLS2_MELODIC, "virtual-child-conform-func", conform_melodic, NULL); ipatch_type_set(IPATCH_TYPE_VIRTUAL_GIG_PERCUSSION, "virtual-child-conform-func", conform_percussion, NULL); ipatch_type_set(IPATCH_TYPE_VIRTUAL_GIG_MELODIC, "virtual-child-conform-func", conform_melodic, NULL); ipatch_type_set(IPATCH_TYPE_VIRTUAL_SF2_PERCUSSION, "virtual-child-conform-func", conform_percussion, NULL); ipatch_type_set(IPATCH_TYPE_VIRTUAL_SF2_MELODIC, "virtual-child-conform-func", conform_melodic, NULL); /* container child sorting */ ipatch_type_set(IPATCH_TYPE_VIRTUAL_DLS2_MELODIC, "sort-children", TRUE, NULL); ipatch_type_set(IPATCH_TYPE_VIRTUAL_DLS2_PERCUSSION, "sort-children", TRUE, NULL); ipatch_type_set(IPATCH_TYPE_VIRTUAL_GIG_MELODIC, "sort-children", TRUE, NULL); ipatch_type_set(IPATCH_TYPE_VIRTUAL_GIG_PERCUSSION, "sort-children", TRUE, NULL); ipatch_type_set(IPATCH_TYPE_VIRTUAL_SF2_MELODIC, "sort-children", TRUE, NULL); ipatch_type_set(IPATCH_TYPE_VIRTUAL_SF2_PERCUSSION, "sort-children", TRUE, NULL); ipatch_type_set(IPATCH_TYPE_VBANK, "sort-children", TRUE, NULL); /* set "splits-type" properties */ ipatch_type_set(IPATCH_TYPE_SF2_PRESET, "splits-type", IPATCH_SPLITS_NORMAL, NULL); ipatch_type_set(IPATCH_TYPE_SF2_INST, "splits-type", IPATCH_SPLITS_NORMAL, NULL); ipatch_type_set(IPATCH_TYPE_DLS2_INST, "splits-type", IPATCH_SPLITS_NORMAL, NULL); ipatch_type_set(IPATCH_TYPE_GIG_INST, "splits-type", IPATCH_SPLITS_NO_OVERLAP, NULL); ipatch_type_set(IPATCH_TYPE_SLI_INST, "splits-type", IPATCH_SPLITS_NORMAL, NULL); ipatch_type_set(IPATCH_TYPE_VBANK_INST, "splits-type", IPATCH_SPLITS_NORMAL, NULL); /* set "mime-type" properties */ ipatch_type_set(IPATCH_TYPE_SF2_FILE, "mime-type", "audio/x-soundfont", NULL); ipatch_type_set(IPATCH_TYPE_DLS_FILE, "mime-type", "audio/dls", NULL); ipatch_type_set(IPATCH_TYPE_GIG_FILE, "mime-type", "audio/x-gigasampler", NULL); ipatch_type_set(IPATCH_TYPE_SLI_FILE, "mime-type", "audio/x-spectralis", NULL); G_UNLOCK(lock_init); } /** * ipatch_deinit: * * Free libInstPatch library. Should be called when the application have * finished. */ static void ipatch_deinit(void) { g_free(ipatch_application_name); /*------------------------------------------------------------------------- Free internal systems -------------------------------------------------------------------------*/ /* Free 'GParamSpec extended properties' system */ _ipatch_param_deinit(); /* Free the 'GObject style properties' system for GTypes */ _ipatch_type_prop_deinit(); /* Free 'unit conversion' system */ _ipatch_unit_deinit(); /* Free object's properties 'encoding/decoding XML handlers' system */ _ipatch_xml_object_deinit(); /* Free GValue constant values */ _ipatch_util_deinit(); /* Free 'SoundFont generators' subsystem */ _ipatch_sf2_gen_deinit(); /*------------------------------------------------------------------------- Free object subsystem (list, hash). These list or hash are specfific to the respective object. Initialization/deinitialization functions are in the respective object module file. Here deinitialization functions _xxx_deinit() are called. Respective initialization functions _xxx_init() are called in ipatch_init(). -------------------------------------------------------------------------*/ /* Free container subsystem */ _ipatch_container_notify_deinit(); /* Free DLS2 subsystem */ _ipatch_DLS2_infos_deinit(); /* Free DLS2 sample subsystem */ _ipatch_DLS2_sample_deinit(); /* Free File subsystem */ _ipatch_file_deinit(); /* Free Item propterty subsystem */ _ipatch_item_deinit(); /* Free Sample data subsystem */ _ipatch_sample_data_deinit(); /* Free Sample store swap recovery subsystem */ _ipatch_sample_store_swap_recover_deinit(); /* Free converter subsytem */ _ipatch_converter_deinit(); /* Free Audio format conversion subsystem */ _ipatch_sample_transform_deinit(); /* Free default mod list */ _ipatch_sf2_mod_list_deinit(); /* Free paste handlers list */ _ipatch_paste_deinit(); } /** * ipatch_close: * * This should be called prior to application close. * * Decrement the reference counter and if it reaches 0 performs cleanup of * libInstPatch, such as deleting temporary files and internal caches. * If the counter is still > 0, the function return without doing cleanup * (the library is still owned). * * Does nothing if the library is already deinitialized (or was not initialized). * Since: 1.1.0 */ void ipatch_close(void) { /* do nothing if the library is already deinitialized */ G_LOCK(lock_init); init_counter--; if(init_counter != 0) { /* library still owned by a task, do nothing */ if(init_counter < 0) { init_counter = 0; } G_UNLOCK(lock_init); return; } ipatch_sample_store_swap_close(); ipatch_deinit(); G_UNLOCK(lock_init); } static gboolean ipatch_strv_xml_encode(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err) { GStrv strv; g_return_val_if_fail(G_VALUE_HOLDS(value, G_TYPE_STRV), FALSE); strv = g_value_get_boxed(value); if(!strv) { ipatch_xml_set_attribute(node, "null", "1"); return (TRUE); } for(; *strv; strv++) { ipatch_xml_new_node(node, "value", *strv, NULL); } return (TRUE); } static gboolean ipatch_strv_xml_decode(GNode *node, GObject *object, GParamSpec *pspec, GValue *value, GError **err) { GStrv strv; GNode *n; int i; g_return_val_if_fail(G_VALUE_HOLDS(value, G_TYPE_STRV), FALSE); if(ipatch_xml_test_attribute(node, "null", "1")) { g_value_set_boxed(value, NULL); return (TRUE); } /* Count "value" child nodes */ for(i = 0, n = node->children; n; n = n->next) if(ipatch_xml_test_name(n, "value")) { i++; } strv = g_new(char *, i + 1); /* ++ alloc new strv array */ for(i = 0, n = node->children; n; n = n->next) { if(!ipatch_xml_test_name(n, "value")) { continue; } strv[i] = ipatch_xml_dup_value(n); i++; } strv[i] = NULL; g_value_take_boxed(value, strv); return (TRUE); } static void virtual_parent_dls2_inst(GType type, GParamSpec *spec, GValue *value, GObject *object) { gboolean percuss = FALSE; if(object) { g_object_get(object, "percussion", &percuss, NULL); } if(percuss) { g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_DLS2_PERCUSSION); } else { g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_DLS2_MELODIC); } } static void virtual_parent_gig_inst(GType type, GParamSpec *spec, GValue *value, GObject *object) { gboolean percuss = FALSE; if(object) { g_object_get(object, "percussion", &percuss, NULL); } if(percuss) { g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_GIG_PERCUSSION); } else { g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_GIG_MELODIC); } } static void virtual_parent_sf2_preset(GType type, GParamSpec *spec, GValue *value, GObject *object) { gboolean percuss = FALSE; if(object) { g_object_get(object, "percussion", &percuss, NULL); } if(percuss) { g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_SF2_PERCUSSION); } else { g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_SF2_MELODIC); } } static void virtual_parent_sf2_sample(GType type, GParamSpec *spec, GValue *value, GObject *object) { gboolean rom = FALSE; if(object) { g_object_get(object, "rom", &rom, NULL); } if(rom) { g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_SF2_ROM); } else { g_value_set_gtype(value, IPATCH_TYPE_VIRTUAL_SF2_SAMPLES); } } static void conform_percussion(GObject *object) { g_object_set(object, "percussion", TRUE, NULL); } static void conform_melodic(GObject *object) { g_object_set(object, "percussion", FALSE, NULL); } /** * ipatch_set_application_name: * @name: Application name and version (example: "swami 1.0") or %NULL to * unset application name * * Set the global application name string which is used as the * software string written to patch files. This string should contain * the name of the application, and its version, that is using * libInstPatch. The libInstPatch version will also be output where * appropriate, so the software string written to a SoundFont for * example would look something like "swami 1.0 (libInstPatch 1.0)". */ void ipatch_set_application_name(const char *name) { if(ipatch_application_name) { g_free(ipatch_application_name); } if(name) { ipatch_application_name = g_strdup(name); } else { ipatch_application_name = NULL; } } /** * ipatch_version: * @major: (out) (optional): Pointer to store major version or %NULL * @minor: (out) (optional): Pointer to store minor version or %NULL * @micro: (out) (optional): Pointer to store micro version or %NULL * * Fetch the runtime version of the libInstPatch library. */ void ipatch_version(guint *major, guint *minor, guint *micro) { if(major) { *major = IPATCH_VERSION_MAJOR; } if(minor) { *minor = IPATCH_VERSION_MINOR; } if(micro) { *micro = IPATCH_VERSION_MICRO; } } GQuark ipatch_error_quark(void) { static GQuark q = 0; if(q == 0) { q = g_quark_from_static_string("libInstPatch-error-quark"); } return (q); } /** * _ret_g_log: (skip) */ int _ret_g_log(const gchar *log_domain, GLogLevelFlags log_level, const gchar *format, ...) { va_list args; va_start(args, format); g_logv(log_domain, log_level, format, args); va_end(args); return (TRUE); } /** * ipatch_gerror_message: (skip) * @err: A GError object or %NULL * * A utility function to check if a GError is set and return the * GError's message field if it is, or a string explaining that there * isn't any error info if @err is %NULL. * * Returns: The GError's message or a "<No detailed error information>" string. */ G_CONST_RETURN char * ipatch_gerror_message(GError *err) { return ((err) ? (err)->message : _("")); } /** * _ipatch_code_error: (skip) * * Internal function used by ipatch_code_error macros */ void _ipatch_code_error(const char *file, guint line, const char *func, GError **err, const char *format, ...) { va_list args; va_start(args, format); _ipatch_code_errorv(file, line, func, err, format, args); va_end(args); } /** * _ipatch_code_errorv: (skip) * * Internal function used by ipatch_code_error macros */ void _ipatch_code_errorv(const char *file, guint line, const char *func, GError **err, const char *format, va_list args) { char *msg, *loc, *temp; if(file && func) { loc = g_strdup_printf("%s:%d:%s()", file, line, func); } else if(file) { loc = g_strdup_printf("%s:%d", file, line); } else { loc = NULL; } temp = g_strdup_vprintf(format, args); msg = g_strdup_printf("%s - %s", loc, temp); g_free(loc); g_free(temp); g_critical("%s", msg); g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_PROGRAM, "Programmer error! (%s)", msg); g_free(msg); } /** * ipatch_strconcat_num: (skip) * @src: Source string * @num: Number to concatenate * @dest: Destination buffer * @size: Size of destination buffer * * Creates a string with a number appended to it but ensures that it is * of the specified @size (including NULL termination). Characters in the * middle of the string are removed and a ".." is inserted, if necessary. */ void ipatch_strconcat_num(const char *src, int num, char *dest, int size) { char numstr[16]; int numlen, srclen, newlen, len1; int remove; sprintf(numstr, "%d", num); numlen = strlen(numstr); srclen = strlen(src); remove = (srclen + numlen) - (size - 1); if(remove > 0) /* any characters need to be removed? */ { remove += 2; /* for ".." */ newlen = srclen - remove; /* new length of non numeric string */ len1 = (newlen + 1) / 2; /* length of first part before ".." */ sprintf(dest, "%.*s..%.*s%s", len1, src, newlen - len1, src + (srclen - (newlen - len1)), numstr); } else { g_stpcpy(g_stpcpy(dest, src), numstr); } } /** * ipatch_dump_object: (skip) * @object: Object to dump * @recursive: Set to %TRUE to recurse the @object children (if its a * #IpatchContainer derived object). * @file: File to dump to or %NULL for stdout * * Dumps object info to a file for debugging purposes. */ void ipatch_dump_object(GObject *object, gboolean recursive, FILE *file) { char indent_buf[64] = ""; g_return_if_fail(G_IS_OBJECT(object)); if(!file) { file = stdout; } if(!recursive) { dump_object_info(object, indent_buf, file); fprintf(file, "\n", g_type_name(G_TYPE_FROM_INSTANCE(object)), object); } else { dump_recursive(object, indent_buf, file); } } static void dump_recursive(GObject *object, char *indent, FILE *file) { dump_object_info(object, indent, file); strcat(indent, " "); /* increase indent */ if(IPATCH_IS_CONTAINER(object)) { /* iterate over children if its an IpatchContainer */ IpatchList *list; IpatchIter iter; GObject *obj; list = ipatch_container_get_children(IPATCH_CONTAINER(object), G_TYPE_OBJECT); /* ++ ref list */ ipatch_list_init_iter(list, &iter); obj = ipatch_iter_first(&iter); if(obj) { fprintf(file, "\n"); } while(obj) { dump_recursive(obj, indent, file); obj = ipatch_iter_next(&iter); } g_object_unref(list); /* -- unref list */ } indent[strlen(indent) - 2] = '\0'; /* decrease indent */ fprintf(file, "%s\n", indent, g_type_name(G_TYPE_FROM_INSTANCE(object))); } static void dump_object_info(GObject *object, char *indent, FILE *file) { GParamSpec **pspecs, **pspec; GValue value = { 0 }; char *contents; fprintf(file, "%s<%s addr=%p>\n", indent, g_type_name(G_TYPE_FROM_INSTANCE(object)), object); fprintf(file, "%s refcount = %u\n", indent, object->ref_count); pspecs = g_object_class_list_properties(G_OBJECT_GET_CLASS(object), NULL); pspec = pspecs; while(*pspec) /* write out property values */ { if((*pspec)->flags & G_PARAM_READABLE) { g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(*pspec)); g_object_get_property(object, g_param_spec_get_name(*pspec), &value); contents = g_strdup_value_contents(&value); g_value_unset(&value); fprintf(file, "%s %s = %s\n", indent, g_param_spec_get_name(*pspec), contents); g_free(contents); } pspec++; } g_free(pspecs); } /** * ipatch_glist_unref_free: (skip) * @objlist: List of GObjects * * Unreference each GObject in a GList and free the list. * * Since: 1.1.0 */ void ipatch_glist_unref_free(GList *objlist) { g_list_foreach(objlist, (GFunc) g_object_unref, NULL); g_list_free(objlist); } libinstpatch-1.1.6/libinstpatch/misc.h000066400000000000000000000055601400263525300200010ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __MISC_H__ #define __MISC_H__ #include #include #include /** * IPATCH_ERROR: (skip) * * libInstPatch domain for g_set_error */ #define IPATCH_ERROR ipatch_error_quark() typedef enum { IPATCH_ERROR_FAIL, /* a general failure */ IPATCH_ERROR_IO, /* I/O error (file operations, etc) */ IPATCH_ERROR_PROGRAM, /* a programming error */ IPATCH_ERROR_INVALID, /* invalid parameter or data */ IPATCH_ERROR_CORRUPT, /* corrupted data */ IPATCH_ERROR_NOMEM, /* out of memory error */ IPATCH_ERROR_UNSUPPORTED, /* unsupported feature */ IPATCH_ERROR_UNEXPECTED_EOF, /* unexpected end of file */ IPATCH_ERROR_UNHANDLED_CONVERSION, /* unhandled object conversion */ IPATCH_ERROR_BUSY /* a resource is busy (still open, etc) - Since: 1.1.0 */ } IpatchError; #ifdef G_HAVE_ISO_VARARGS #define ipatch_code_error(...) \ _ipatch_code_error (__FILE__, __LINE__, G_GNUC_PRETTY_FUNCTION, __VA_ARGS__) #elif defined(G_HAVE_GNUC_VARARGS) #define ipatch_code_error(err, format...) \ _ipatch_code_error (__FILE__, __LINE__, G_GNUC_PRETTY_FUNCTION, err, format) #else /* no varargs macros */ static void ipatch_code_error(GError **err, const char *format, ...) { va_list args; va_start(args, format); _ipatch_code_errorv(NULL, -1, NULL, err, format, args); va_end(args); } #endif extern char *ipatch_application_name; void ipatch_init(void); void ipatch_close(void); void ipatch_set_application_name(const char *name); GQuark ipatch_error_quark(void); G_CONST_RETURN char *ipatch_gerror_message(GError *err); void _ipatch_code_error(const char *file, guint line, const char *func, GError **err, const char *format, ...); void _ipatch_code_errorv(const char *file, guint line, const char *func, GError **err, const char *format, va_list args); void ipatch_strconcat_num(const char *src, int num, char *dest, int size); void ipatch_dump_object(GObject *object, gboolean recursive, FILE *file); void ipatch_glist_unref_free(GList *objlist); #endif libinstpatch-1.1.6/libinstpatch/sample.c000066400000000000000000000650431400263525300203240ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: sample * @short_description: Audio sample format conversion functions and defines. * @see_also: * @stability: Stable * * This module provides functions for converting audio formats as well as a system * for defining audio formats with a single integer composed of multiple fields * for sample width, channel count, sign and endian byte order. */ #include #include #include "sample.h" #include "IpatchSampleTransform.h" /* NOTES: * 24 bit is converted to 4 byte integers first (less of a pain) * Floating point audio is assumed to have a range of -1.0 to 1.0. */ #define NOP (void)0; #define TRANS_FUNC(NAME, INTYPE, OUTTYPE, PRE, CODE, POST) \ void TFF_ ## NAME (IpatchSampleTransform *transform) \ { \ INTYPE *inp = transform->buf1; \ OUTTYPE *outp = transform->buf2; \ guint i, count = transform->samples; \ PRE \ \ for (i = 0; i < count; i++) \ CODE; \ POST \ } /* float transforms */ TRANS_FUNC(floattodouble, gfloat, gdouble, NOP, { outp[i] = inp[i]; }, NOP) TRANS_FUNC(doubletofloat, gdouble, gfloat, NOP, { outp[i] = (gfloat)inp[i]; }, NOP) /* signed bit width change funcs */ TRANS_FUNC(s8to16, gint8, gint16, NOP, { outp[i] = (gint16)inp[i] << 8; }, NOP) TRANS_FUNC(s8to24, gint8, gint32, NOP, { outp[i] = (gint32)inp[i] << 16; }, NOP) TRANS_FUNC(s8to32, gint8, gint32, NOP, { outp[i] = (gint32)inp[i] << 24; }, NOP) TRANS_FUNC(s8tofloat, gint8, gfloat, NOP, { outp[i] = inp[i] / (float)128.0; }, NOP) TRANS_FUNC(s8todouble, gint8, gdouble, NOP, { outp[i] = inp[i] / (double)128.0; }, NOP) TRANS_FUNC(s16to8, gint16, gint8, NOP, { outp[i] = inp[i] >> 8; }, NOP) TRANS_FUNC(s16to24, gint16, gint32, NOP, { outp[i] = inp[i] << 8; }, NOP) TRANS_FUNC(s16to32, gint16, gint32, NOP, { outp[i] = inp[i] << 16; }, NOP) TRANS_FUNC(s16tofloat, gint16, gfloat, NOP, { outp[i] = inp[i] / (float)32768.0; }, NOP) TRANS_FUNC(s16todouble, gint16, gdouble, NOP, { outp[i] = inp[i] / (double)32768.0; }, NOP) TRANS_FUNC(s24to8, gint32, gint8, NOP, { outp[i] = inp[i] >> 16; }, NOP) TRANS_FUNC(s24to16, gint32, gint16, NOP, { outp[i] = inp[i] >> 8; }, NOP) TRANS_FUNC(s24to32, gint32, gint32, NOP, { outp[i] = inp[i] << 8; }, NOP) TRANS_FUNC(s24tofloat, gint32, gfloat, NOP, { outp[i] = inp[i] / (float)8388608.0; }, NOP) TRANS_FUNC(s24todouble, gint32, gdouble, NOP, { outp[i] = inp[i] / (double)8388608.0; }, NOP) TRANS_FUNC(s32to8, gint32, gint8, NOP, { outp[i] = inp[i] >> 24; }, NOP) TRANS_FUNC(s32to16, gint32, gint16, NOP, { outp[i] = inp[i] >> 16; }, NOP) TRANS_FUNC(s32to24, gint32, gint32, NOP, { outp[i] = inp[i] >> 8; }, NOP) TRANS_FUNC(s32tofloat, gint32, gfloat, NOP, { outp[i] = inp[i] / (float)2147483648.0; }, NOP) TRANS_FUNC(s32todouble, gint32, gdouble, NOP, { outp[i] = inp[i] / (double)2147483648.0; }, NOP) TRANS_FUNC(floattos8, gfloat, gint8, NOP, { outp[i] = (gint8)(inp[i] * 127.0); }, NOP) TRANS_FUNC(floattos16, gfloat, gint16, NOP, { outp[i] = (gint16)(inp[i] * 32767.0); }, NOP) TRANS_FUNC(floattos24, gfloat, gint32, NOP, { outp[i] = (gint32)(inp[i] * 8388607.0); }, NOP) TRANS_FUNC(floattos32, gfloat, gint32, NOP, { outp[i] = (gint32)(inp[i] * 2147483647.0); }, NOP) TRANS_FUNC(doubletos8, gdouble, gint8, NOP, { outp[i] = (gint8)(inp[i] * 127.0); }, NOP) TRANS_FUNC(doubletos16, gdouble, gint16, NOP, { outp[i] = (gint16)(inp[i] * 32767.0); }, NOP) TRANS_FUNC(doubletos24, gdouble, gint32, NOP, { outp[i] = (gint32)(inp[i] * 8388607.0); }, NOP) TRANS_FUNC(doubletos32, gdouble, gint32, NOP, { outp[i] = (gint32)(inp[i] * 2147483647.0); }, NOP) /* unsigned bit width change funcs */ TRANS_FUNC(u8to16, guint8, guint16, NOP, { outp[i] = inp[i] << 8; }, NOP) TRANS_FUNC(u8to24, guint8, guint32, NOP, { outp[i] = inp[i] << 16; }, NOP) TRANS_FUNC(u8to32, guint8, guint32, NOP, { outp[i] = inp[i] << 24; }, NOP) TRANS_FUNC(u8tofloat, guint8, gfloat, NOP, { outp[i] = (gint8)(inp[i] ^ 0x80) / (float)128.0; }, NOP) TRANS_FUNC(u8todouble, guint8, gdouble, NOP, { outp[i] = (gint8)(inp[i] ^ 0x80) / (double)128.0; }, NOP) TRANS_FUNC(u16to8, guint16, guint8, NOP, { outp[i] = inp[i] >> 8; }, NOP) TRANS_FUNC(u16to24, guint16, guint32, NOP, { outp[i] = inp[i] << 8; }, NOP) TRANS_FUNC(u16to32, guint16, guint32, NOP, { outp[i] = inp[i] << 16; }, NOP) TRANS_FUNC(u16tofloat, guint16, gfloat, NOP, { outp[i] = (gint16)(inp[i] ^ 0x8000) / (float)32768.0; }, NOP) TRANS_FUNC(u16todouble, guint16, gdouble, NOP, { outp[i] = (gint16)(inp[i] ^ 0x8000) / (double)32768.0; }, NOP) TRANS_FUNC(u24to8, guint32, guint8, NOP, { outp[i] = inp[i] >> 16; }, NOP) TRANS_FUNC(u24to16, guint32, guint16, NOP, { outp[i] = inp[i] >> 8; }, NOP) TRANS_FUNC(u24to32, guint32, guint32, NOP, { outp[i] = inp[i] << 8; }, NOP) TRANS_FUNC(u24tofloat, guint32, gfloat, NOP, { outp[i] = ((gint32)inp[i] - 0x800000) / (float)8388608.0; }, NOP) TRANS_FUNC(u24todouble, guint32, gdouble, NOP, { outp[i] = ((gint32)inp[i] - 0x800000) / (double)8388608.0; }, NOP) TRANS_FUNC(u32to8, guint32, guint8, NOP, { outp[i] = inp[i] >> 24; }, NOP) TRANS_FUNC(u32to16, guint32, guint16, NOP, { outp[i] = inp[i] >> 16; }, NOP) TRANS_FUNC(u32to24, guint32, guint32, NOP, { outp[i] = inp[i] >> 8; }, NOP) TRANS_FUNC(u32tofloat, guint32, gfloat, NOP, { outp[i] = (gint32)(inp[i] ^ 0x80000000) / (float)2147483648.0; }, NOP) TRANS_FUNC(u32todouble, guint32, gdouble, NOP, { outp[i] = (gint32)(inp[i] ^ 0x80000000) / (double)2147483648.0; }, NOP) TRANS_FUNC(floattou8, gfloat, guint8, NOP, { outp[i] = (guint8)((inp[i] + 1.0) * 127.5 + 0.5); }, NOP) TRANS_FUNC(floattou16, gfloat, guint16, NOP, { outp[i] = (guint16)((inp[i] + 1.0) * 32767.5 + 0.5); }, NOP) TRANS_FUNC(floattou24, gfloat, guint32, NOP, { outp[i] = (guint32)((inp[i] + 1.0) * 8388607.5 + 0.5); }, NOP) TRANS_FUNC(floattou32, gfloat, guint32, NOP, { outp[i] = (guint32)((inp[i] + 1.0) * 2147483647.5 + 0.5); }, NOP) TRANS_FUNC(doubletou8, gdouble, guint8, NOP, { outp[i] = (guint8)((inp[i] + 1.0) * 127.5 + 0.5); }, NOP) TRANS_FUNC(doubletou16, gdouble, guint16, NOP, { outp[i] = (guint16)((inp[i] + 1.0) * 32767.5 + 0.5); }, NOP) TRANS_FUNC(doubletou24, gdouble, guint32, NOP, { outp[i] = (guint32)((inp[i] + 1.0) * 8388607.5 + 0.5); }, NOP) TRANS_FUNC(doubletou32, gdouble, guint32, NOP, { outp[i] = (guint32)((inp[i] + 1.0) * 2147483647.5 + 0.5); }, NOP) /* sign changer funcs (24 bit in 4 byte integers requires 2 separate funcs) */ TRANS_FUNC(togsign8, guint8, guint8, NOP, { outp[i] = inp[i] ^ 0x80; }, NOP) TRANS_FUNC(togsign16, guint16, guint16, NOP, { outp[i] = inp[i] ^ 0x8000; }, NOP) TRANS_FUNC(signtou24, guint32, guint32, NOP, { outp[i] = inp[i] + 0x800000; }, NOP) TRANS_FUNC(unsigntos24, gint32, gint32, NOP, { outp[i] = ((inp[i] ^ 0x800000) << 8) >> 8; }, NOP) TRANS_FUNC(togsign32, guint32, guint32, NOP, { outp[i] = inp[i] ^ 0x80000000; }, NOP) /* endian swapping funcs */ TRANS_FUNC(swap16, guint16, guint16, guint16 t;, { t = inp[i]; outp[i] = t << 8 | t >> 8; }, NOP) TRANS_FUNC(swap32, guint32, guint32, guint32 t;, { t = inp[i]; outp[i] = ((t & 0xFF) << 24) | ((t & 0xFF00) << 8) | ((t & 0xFF0000) >> 8) | ((t & 0xFF000000) >> 24); }, NOP) TRANS_FUNC(swap64, guint64, guint64, guint64 t;, { t = inp[i]; outp[i] = ((t & 0xFF) << 56) | ((t & 0xFF00) << 40) | ((t & 0xFF0000) << 24) | ((t & 0xFF000000) << 8) | ((t & G_GINT64_CONSTANT(0xFF00000000U)) >> 8) | ((t & G_GINT64_CONSTANT(0xFF0000000000U)) >> 24) | ((t & G_GINT64_CONSTANT(0xFF000000000000U)) >> 40) | ((t & G_GINT64_CONSTANT(0xFF00000000000000U)) >> 56); }, NOP) /* funcs for converting between real signed 24 bit (3 byte) and 4 byte words */ /* signed little endian 3 bytes to 4 bytes */ TRANS_FUNC(sle3bto4b, guint8, guint32, guint i2 = 0;, { outp[i] = inp[i2] | (inp[i2 + 1] << 8) | (inp[i2 + 2] << 16) | ((inp[i2 + 2] & 0x80) ? 0xFF000000 : 0); i2 += 3; }, NOP) /* signed big endian 3 bytes to 4 bytes */ TRANS_FUNC(sbe3bto4b, guint8, guint32, guint i2 = 0;, { outp[i] = inp[i2 + 2] | (inp[i2 + 1] << 8) | (inp[i2] << 16) | ((inp[i2] & 0x80) ? 0xFF000000 : 0); i2 += 3; }, NOP) /* 4 bytes to signed little endian 3 bytes */ TRANS_FUNC(4btosle3b, guint32, guint8, guint i2 = 0; gint32 t;, { t = inp[i]; outp[i2] = t; outp[i2 + 1] = t >> 8; outp[i2 + 2] = t >> 16; i2 += 3; }, NOP) /* 4 bytes to signed big endian 3 bytes */ TRANS_FUNC(4btosbe3b, guint32, guint8, guint i2 = 0; gint32 t;, { t = inp[i]; outp[i2 + 2] = t; outp[i2 + 1] = t >> 8; outp[i2] = t >> 16; i2 += 3; }, NOP) /* funcs for converting between real unsigned 24 bit (3 byte) and 4 byte words */ /* unsigned little endian 3 bytes to 4 bytes */ TRANS_FUNC(ule3bto4b, guint8, guint32, guint i2 = 0;, { outp[i] = inp[i2] | (inp[i2 + 1] << 8) | (inp[i2 + 2] << 16); i2 += 3; }, NOP) /* unsigned big endian 3 bytes to 4 bytes */ TRANS_FUNC(ube3bto4b, guint8, guint32, guint i2 = 0;, { outp[i] = inp[i2 + 2] | (inp[i2 + 1] << 8) | (inp[i2] << 16); i2 += 3; }, NOP) /* 4 bytes to unsigned little endian 3 bytes */ TRANS_FUNC(4btoule3b, guint32, guint8, guint i2 = 0; guint32 t;, { t = inp[i]; outp[i2] = t; outp[i2 + 1] = t >> 8; outp[i2 + 2] = t >> 16; i2 += 3; }, NOP) /* 4 bytes to unsigned big endian 3 bytes */ TRANS_FUNC(4btoube3b, guint32, guint8, guint i2 = 0; guint32 t;, { t = inp[i]; outp[i2 + 2] = t; outp[i2 + 1] = t >> 8; outp[i2] = t >> 16; i2 += 3; }, NOP) /* mono to stereo transforms */ TRANS_FUNC(8mtos, guint8, guint8, NOP, { outp[i << 1] = inp[i]; outp[(i << 1) + 1] = inp[i]; }, transform->samples = count << 1;) TRANS_FUNC(16mtos, guint16, guint16, NOP, { outp[i << 1] = inp[i]; outp[(i << 1) + 1] = inp[i]; }, transform->samples = count << 1;) TRANS_FUNC(32mtos, guint32, guint32, NOP, { outp[i << 1] = inp[i]; outp[(i << 1) + 1] = inp[i]; }, transform->samples = count << 1;) TRANS_FUNC(64mtos, guint64, guint64, NOP, { outp[i << 1] = inp[i]; outp[(i << 1) + 1] = inp[i]; }, transform->samples = count << 1;) /* stereo to left transforms */ TRANS_FUNC(8stol, guint8, guint8, count >>= 1;, { outp[i] = inp[i << 1]; }, transform->samples = count;) TRANS_FUNC(16stol, guint16, guint16, count >>= 1;, { outp[i] = inp[i << 1]; }, transform->samples = count;) TRANS_FUNC(32stol, guint32, guint32, count >>= 1;, { outp[i] = inp[i << 1]; }, transform->samples = count;) TRANS_FUNC(64stol, guint64, guint64, count >>= 1;, { outp[i] = inp[i << 1]; }, transform->samples = count;) /* stereo to right transforms */ TRANS_FUNC(8stor, guint8, guint8, count >>= 1;, { outp[i] = inp[(i << 1) + 1]; }, transform->samples = count;) TRANS_FUNC(16stor, guint16, guint16, count >>= 1;, { outp[i] = inp[(i << 1) + 1]; }, transform->samples = count;) TRANS_FUNC(32stor, guint32, guint32, count >>= 1;, { outp[i] = inp[(i << 1) + 1]; }, transform->samples = count;) TRANS_FUNC(64stor, guint64, guint64, count >>= 1;, { outp[i] = inp[(i << 1) + 1]; }, transform->samples = count;) /* arbitrary channel mapping */ TRANS_FUNC(8chanmap, guint8, guint8, int schans = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(transform->src_format); \ int dchans = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(transform->dest_format); \ int di; int spos = 0; int dpos = 0;, { \ for(di = 0; di < dchans; di++, dpos++) \ outp[dpos] = inp[spos + transform->channel_map[di]]; \ spos += schans; \ }, NOP) TRANS_FUNC(16chanmap, guint16, guint16, int schans = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(transform->src_format); \ int dchans = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(transform->dest_format); \ int di; int spos = 0; int dpos = 0;, { \ for(di = 0; di < dchans; di++, dpos++) \ outp[dpos] = inp[spos + transform->channel_map[di]]; \ spos += schans; \ }, NOP) TRANS_FUNC(32chanmap, guint32, guint32, int schans = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(transform->src_format); \ int dchans = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(transform->dest_format); \ int di; int spos = 0; int dpos = 0;, { \ for(di = 0; di < dchans; di++, dpos++) \ outp[dpos] = inp[spos + transform->channel_map[di]]; \ spos += schans; \ }, NOP) TRANS_FUNC(64chanmap, guint64, guint64, int schans = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(transform->src_format); \ int dchans = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(transform->dest_format); \ int di; int spos = 0; int dpos = 0;, { \ for(di = 0; di < dchans; di++, dpos++) \ outp[dpos] = inp[spos + transform->channel_map[di]]; \ spos += schans; \ }, NOP) /* signed transform func matrix [infmt][outfmt] */ static IpatchSampleTransformFunc signed_tff[6][6] = { { NULL, TFF_s8to16, TFF_s8to24, TFF_s8to32, TFF_s8tofloat, TFF_s8todouble }, { TFF_s16to8, NULL, TFF_s16to24, TFF_s16to32, TFF_s16tofloat, TFF_s16todouble }, { TFF_s24to8, TFF_s24to16, NULL, TFF_s24to32, TFF_s24tofloat, TFF_s24todouble }, { TFF_s32to8, TFF_s32to16, TFF_s32to24, NULL, TFF_s32tofloat, TFF_s32todouble }, { TFF_floattos8, TFF_floattos16, TFF_floattos24, TFF_floattos32, NULL, TFF_floattodouble }, { TFF_doubletos8, TFF_doubletos16, TFF_doubletos24, TFF_doubletos32, TFF_doubletofloat, NULL } }; /* unsiged transform func matrix [infmt][outfmt] */ static IpatchSampleTransformFunc unsigned_tff[6][6] = { { NULL, TFF_u8to16, TFF_u8to24, TFF_u8to32, TFF_u8tofloat, TFF_u8todouble }, { TFF_u16to8, NULL, TFF_u16to24, TFF_u16to32, TFF_u16tofloat, TFF_u16todouble }, { TFF_u24to8, TFF_u24to16, NULL, TFF_u24to32, TFF_u24tofloat, TFF_u24todouble }, { TFF_u32to8, TFF_u32to16, TFF_u32to24, NULL, TFF_u32tofloat, TFF_u32todouble }, { TFF_floattou8, TFF_floattou16, TFF_floattou24, TFF_floattou32, NULL, TFF_floattodouble }, { TFF_doubletou8, TFF_doubletou16, TFF_doubletou24, TFF_doubletou32, TFF_doubletofloat, NULL } }; /* sign toggle transform functions */ static IpatchSampleTransformFunc sign_tff[6] = { TFF_togsign8, TFF_togsign16, NULL, TFF_togsign32, NULL, NULL }; /* endian swap functions */ static IpatchSampleTransformFunc swap_tff[6] = { NULL, TFF_swap16, TFF_swap32, TFF_swap32, TFF_swap32, TFF_swap64 }; /* mono to stereo transform functions */ static IpatchSampleTransformFunc mono_to_stereo_tff[6] = { TFF_8mtos, TFF_16mtos, TFF_32mtos, TFF_32mtos, TFF_32mtos, TFF_64mtos }; /* stereo to left transform functions */ static IpatchSampleTransformFunc stereo_to_left_tff[6] = { TFF_8stol, TFF_16stol, TFF_32stol, TFF_32stol, TFF_32stol, TFF_64stol }; /* stereo to right transform functions */ static IpatchSampleTransformFunc stereo_to_right_tff[6] = { TFF_8stor, TFF_16stor, TFF_32stor, TFF_32stor, TFF_32stor, TFF_64stor }; /* arbitrary transform functions */ static IpatchSampleTransformFunc chanmap_tff[6] = { TFF_8chanmap, TFF_16chanmap, TFF_32chanmap, TFF_32chanmap, TFF_32chanmap, TFF_64chanmap }; /* IpatchSampleWidth format sizes in bytes (last 8 reserved) */ guint ipatch_sample_width_sizes[16] = { 0, 1, 2, 4, 4, 4, 8, 3, 0, 0, 0, 0, 0, 0, 0, 0 }; static inline void update_max_size(int *curmax, int format); /** * ipatch_sample_format_bit_width: * @format: Sample format * * Like ipatch_sample_format_width() but gets the effective bit width of the * format. Of note is this is not always equivelant to the format width * 8. * For example: #IPATCH_SAMPLE_FLOAT has an effective bit width of 23, * #IPATCH_SAMPLE_24BIT has an effective bit width of 24 but is stored in 32 * bits. This function is really only useful for comparing the relative * "quality" of formats, and the actual returned values may change in the * future. * * Returns: Effective bit width of format. */ int ipatch_sample_format_bit_width(int format) { int width; width = IPATCH_SAMPLE_FORMAT_GET_WIDTH(format); switch(width) { case IPATCH_SAMPLE_FLOAT: /* actually 24 with the sign bit, but we set it to 23 to be less than 24 bit integer audio */ return (23); case IPATCH_SAMPLE_DOUBLE: return (52); case IPATCH_SAMPLE_REAL24BIT: return (24); break; default: return (width * 8); } } /** * ipatch_sample_format_verify: * @format: Sample format (#IpatchSampleWidth | #IpatchSampleSign * | #IpatchSampleEndian | #IpatchSampleChannel). * * Verify a sample format integer. * * Returns: %TRUE if valid, %FALSE otherwise */ gboolean ipatch_sample_format_verify(int format) { int width; width = format & IPATCH_SAMPLE_WIDTH_MASK; if(width < IPATCH_SAMPLE_8BIT || width > IPATCH_SAMPLE_REAL24BIT) { return (FALSE); } if(IPATCH_SAMPLE_FORMAT_IS_UNSIGNED(format) && (width == IPATCH_SAMPLE_FLOAT || width == IPATCH_SAMPLE_DOUBLE)) { return (FALSE); } if(IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format) >= IPATCH_SAMPLE_MAX_CHANNELS) { return (FALSE); } return (TRUE); } /** * ipatch_sample_format_transform_verify: * @src_format: Source sample format * @dest_format: Destination sample format * @channel_map: Channel mapping (use #IPATCH_SAMPLE_UNITY_CHANNEL_MAP * to map all input channels to the same output channels, see * #IPATCH_SAMPLE_MAP_CHANNEL macro for constructing channel map values) * * Verify source and destination sample formats and channel map for a sample * transform operation. * * Returns: %TRUE on success, %FALSE otherwise */ gboolean ipatch_sample_format_transform_verify(int src_format, int dest_format, guint32 channel_map) { int src_chans, dest_chans, i; if(!ipatch_sample_format_verify(src_format) || !ipatch_sample_format_verify(dest_format)) { return (FALSE); } src_chans = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(src_format); dest_chans = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(dest_format); for(i = 0; i < dest_chans; i++) if(((channel_map >> (i * 3)) & 0x07) >= (guint32)src_chans) { return (FALSE); } return (TRUE); } /** * ipatch_sample_get_transform_funcs: (skip) * @src_format: Source audio format to convert from * @dest_format: Destination audio format to convert to * @channel_map: Channel mapping (use #IPATCH_SAMPLE_UNITY_CHANNEL_MAP * to map all input channels to the same output channels, 3 bits times * #IPATCH_SAMPLE_MAX_CHANNELS (8) = 24 bits total, see * #IPATCH_SAMPLE_MAP_CHANNEL macro for constructing channel map values) * @buf1_max_frame: Output - maximum sample frame size for first buffer * @buf2_max_frame: Output - maximum sample frame size for second buffer * @funcs: Caller provided array to store transform functions to. It should * have at least #IPATCH_SAMPLE_MAX_TRANSFORM_FUNCS elements. * * Get transform function array for converting from @src_format to * @dest_format. * * Returns: Count of function pointers stored to @funcs. Can be 0 if no * transform is required. */ guint ipatch_sample_get_transform_funcs(int src_format, int dest_format, guint32 channel_map, guint *buf1_max_frame, guint *buf2_max_frame, IpatchSampleTransformFunc *funcs) { int swidth, dwidth, curfmt, schan, dchan; int func_index = 0; int max[2] = { 0 }; /* max frame sizes for buffers */ g_return_val_if_fail(ipatch_sample_format_verify(src_format), 0); g_return_val_if_fail(ipatch_sample_format_verify(dest_format), 0); g_return_val_if_fail(funcs != NULL, 0); if(buf1_max_frame) { *buf1_max_frame = 0; } if(buf2_max_frame) { *buf2_max_frame = 0; } swidth = src_format & IPATCH_SAMPLE_WIDTH_MASK; dwidth = dest_format & IPATCH_SAMPLE_WIDTH_MASK; schan = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(src_format); dchan = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(dest_format); curfmt = src_format; /* init max frame size of first buffer to input frame size */ max[0] = ipatch_sample_format_size(curfmt); /* convert to 4 byte 24 bit data before 3 byte */ if(G_UNLIKELY(dwidth == IPATCH_SAMPLE_REAL24BIT)) { dwidth = IPATCH_SAMPLE_24BIT; } /* if 3 byte 24 bit data, convert to native endian 4 byte integers */ if(G_UNLIKELY(swidth == IPATCH_SAMPLE_REAL24BIT)) { if(IPATCH_SAMPLE_FORMAT_IS_LENDIAN(src_format)) funcs[func_index++] = IPATCH_SAMPLE_FORMAT_IS_SIGNED(src_format) ? TFF_sle3bto4b : TFF_ule3bto4b; else funcs[func_index++] = IPATCH_SAMPLE_FORMAT_IS_SIGNED(src_format) ? TFF_sbe3bto4b : TFF_ube3bto4b; swidth = IPATCH_SAMPLE_24BIT; curfmt = (curfmt & ~IPATCH_SAMPLE_WIDTH_MASK) | IPATCH_SAMPLE_24BIT; update_max_size(&max[func_index & 1], curfmt); } /* if converting from more channels to less */ if(G_UNLIKELY(dchan < schan)) { if(G_LIKELY(dchan == 1 && schan == 2)) /* Stereo to mono mapping? */ { /* Stereo to left map? */ if(IPATCH_SAMPLE_MAP_GET_CHANNEL(channel_map, 0) == 0) { funcs[func_index++] = stereo_to_left_tff[swidth - 1]; } else /* Stereo to right */ { funcs[func_index++] = stereo_to_right_tff[swidth - 1]; } } else { funcs[func_index++] = chanmap_tff[swidth - 1]; } curfmt = (curfmt & ~IPATCH_SAMPLE_CHANNEL_MASK) | (dchan - 1); update_max_size(&max[func_index & 1], curfmt); } /* source format differs from host byte order? - swap REAL24BIT is already swapped by 3 to 4 byte conversion above */ if(G_UNLIKELY (IPATCH_SAMPLE_FORMAT_IS_LENDIAN(src_format) != (G_BYTE_ORDER == G_LITTLE_ENDIAN) && swap_tff[swidth - 1] && (src_format & IPATCH_SAMPLE_WIDTH_MASK) != IPATCH_SAMPLE_REAL24BIT)) { funcs[func_index++] = swap_tff[swidth - 1]; curfmt ^= IPATCH_SAMPLE_BENDIAN; update_max_size(&max[func_index & 1], curfmt); } /* if src and dest aren't floating point and sign differs - toggle sign */ if(G_UNLIKELY (swidth != IPATCH_SAMPLE_FLOAT && swidth != IPATCH_SAMPLE_DOUBLE && dwidth != IPATCH_SAMPLE_FLOAT && dwidth != IPATCH_SAMPLE_DOUBLE && IPATCH_SAMPLE_FORMAT_IS_SIGNED(src_format) != IPATCH_SAMPLE_FORMAT_IS_SIGNED(dest_format))) { if(G_UNLIKELY(swidth == IPATCH_SAMPLE_24BIT)) funcs[func_index++] = IPATCH_SAMPLE_FORMAT_IS_SIGNED(src_format) ? TFF_signtou24 : TFF_unsigntos24; else { funcs[func_index++] = sign_tff[swidth - 1]; } curfmt ^= IPATCH_SAMPLE_UNSIGNED; update_max_size(&max[func_index & 1], curfmt); } if(G_LIKELY(swidth != dwidth)) /* format differs? */ { if(G_UNLIKELY(IPATCH_SAMPLE_FORMAT_IS_FLOATING(curfmt))) { if(IPATCH_SAMPLE_FORMAT_IS_SIGNED(dest_format)) { funcs[func_index++] = signed_tff[swidth - 1][dwidth - 1]; } else /* unsigned */ { funcs[func_index++] = unsigned_tff[swidth - 1][dwidth - 1]; } } else { if(IPATCH_SAMPLE_FORMAT_IS_SIGNED(curfmt)) /* signed transform? */ { funcs[func_index++] = signed_tff[swidth - 1][dwidth - 1]; } else /* unsigned */ { funcs[func_index++] = unsigned_tff[swidth - 1][dwidth - 1]; } } curfmt = (curfmt & ~IPATCH_SAMPLE_WIDTH_MASK) | dwidth; update_max_size(&max[func_index & 1], curfmt); } /* destination format differs from host byte order? - swap REAL24BIT is swapped below */ if(G_UNLIKELY (IPATCH_SAMPLE_FORMAT_IS_LENDIAN(dest_format) != (G_BYTE_ORDER == G_LITTLE_ENDIAN) && swap_tff[dwidth - 1] && (dest_format & IPATCH_SAMPLE_WIDTH_MASK) != IPATCH_SAMPLE_REAL24BIT)) { funcs[func_index++] = swap_tff[dwidth - 1]; curfmt ^= IPATCH_SAMPLE_BENDIAN; update_max_size(&max[func_index & 1], curfmt); } /* if converting from less channels to more */ if(G_UNLIKELY(dchan > schan)) { if(G_LIKELY(dchan == 2 && schan == 1)) /* Mono to stereo mapping? */ { funcs[func_index++] = mono_to_stereo_tff[dwidth - 1]; } else { funcs[func_index++] = chanmap_tff[dwidth - 1]; /* Arbitrary channel mapping */ } curfmt = (curfmt & ~IPATCH_SAMPLE_CHANNEL_MASK) | (dchan - 1); update_max_size(&max[func_index & 1], curfmt); } /* OPTME - Could create channel transform funcs for real 24 bit */ /* if destination is 3 byte 24 bit data then convert to it */ if(G_UNLIKELY ((dest_format & IPATCH_SAMPLE_WIDTH_MASK) == IPATCH_SAMPLE_REAL24BIT)) { if(IPATCH_SAMPLE_FORMAT_IS_LENDIAN(dest_format)) funcs[func_index++] = IPATCH_SAMPLE_FORMAT_IS_SIGNED(src_format) ? TFF_4btosle3b : TFF_4btoule3b; else funcs[func_index++] = IPATCH_SAMPLE_FORMAT_IS_SIGNED(src_format) ? TFF_4btosbe3b : TFF_4btoube3b; curfmt = (curfmt & ~IPATCH_SAMPLE_WIDTH_MASK) | IPATCH_SAMPLE_REAL24BIT; update_max_size(&max[func_index & 1], curfmt); } if(buf1_max_frame) { *buf1_max_frame = max[0]; } if(buf2_max_frame) { *buf2_max_frame = max[1]; } return (func_index); } static inline void update_max_size(int *curmax, int format) { int bytewidth; bytewidth = ipatch_sample_format_size(format); if(bytewidth > *curmax) { *curmax = bytewidth; } } libinstpatch-1.1.6/libinstpatch/sample.h000066400000000000000000000243121400263525300203230ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __SAMPLE_H__ #define __SAMPLE_H__ #include /** * IPATCH_SAMPLE_MAX_TRANSFORM_FUNCS: (skip) * * Maximum number of transform functions returned by * ipatch_sample_get_transform_funcs(). Is larger than current actual maximum * to allow for future backwards compatible expansion (8 is the real current * maximum). */ #define IPATCH_SAMPLE_MAX_TRANSFORM_FUNCS 16 #include /** * IPATCH_SAMPLE_FORMAT_MASK: * * Mask for all fields of sample format integers (width, sign, endian, channel). */ #define IPATCH_SAMPLE_FORMAT_MASK 0x1FF /** * IPATCH_SAMPLE_FORMAT_BITCOUNT: * * Number of bits used for sample format integers. */ #define IPATCH_SAMPLE_FORMAT_BITCOUNT 9 #define IPATCH_SAMPLE_WIDTH_MASK 0x00F /* total of 16 formats (8 reserved) */ #define IPATCH_SAMPLE_CHANNEL_MASK 0x070 /* channel count (8 channels max) */ #define IPATCH_SAMPLE_SIGN_MASK 0x080 /* sign or unsigned (for PCM formats) */ #define IPATCH_SAMPLE_ENDIAN_MASK 0x100 /* endian byte order */ #define IPATCH_SAMPLE_WIDTH_SHIFT 0 #define IPATCH_SAMPLE_CHANNEL_SHIFT 4 #define IPATCH_SAMPLE_SIGN_SHIFT 7 #define IPATCH_SAMPLE_ENDIAN_SHIFT 8 /** * IpatchSampleWidth: * @IPATCH_SAMPLE_INVALID: Invalid format (so 0 can be used to indicate a NULL state) * @IPATCH_SAMPLE_8BIT: 8 bit integer PCM * @IPATCH_SAMPLE_16BIT: 16 bit integer PCM * @IPATCH_SAMPLE_24BIT: 24 bit integer PCM (32 bit ints) * @IPATCH_SAMPLE_32BIT: 32 bit integer PCM * @IPATCH_SAMPLE_FLOAT: 32 bit IEEE float (-1.0 - 1.0) * @IPATCH_SAMPLE_DOUBLE: 64 bit IEEE double (-1.0 - 1.0) * @IPATCH_SAMPLE_REAL24BIT: Real 3 byte 24 bit data (not padded to 32 bits) * * Sample data widths/formats. */ typedef enum { IPATCH_SAMPLE_INVALID = 0, IPATCH_SAMPLE_BIT8 = 1, IPATCH_SAMPLE_BIT16 = 2, IPATCH_SAMPLE_BIT24 = 3, IPATCH_SAMPLE_BIT32 = 4, IPATCH_SAMPLE_FLOAT = 5, IPATCH_SAMPLE_DOUBLE = 6, IPATCH_SAMPLE_REAL24BIT = 7 } IpatchSampleWidth; /* Renamed to be GObject Introspection friendly (symbols can't start with numbers). * Backwards compatible defines below. */ /** * IPATCH_SAMPLE_8BIT: (skip) */ #define IPATCH_SAMPLE_8BIT IPATCH_SAMPLE_BIT8 /** * IPATCH_SAMPLE_16BIT: (skip) */ #define IPATCH_SAMPLE_16BIT IPATCH_SAMPLE_BIT16 /** * IPATCH_SAMPLE_24BIT: (skip) */ #define IPATCH_SAMPLE_24BIT IPATCH_SAMPLE_BIT24 /** * IPATCH_SAMPLE_32BIT: (skip) */ #define IPATCH_SAMPLE_32BIT IPATCH_SAMPLE_BIT32 /** * IpatchSampleChannel: * @IPATCH_SAMPLE_MONO: Mono audio * @IPATCH_SAMPLE_STEREO: Stereo audio * * Descriptive enums for common audio channel configurations. These values * are actually channel count - 1 (0 = mono, 1 = stereo, etc) and can be compared * with the macro IPATCH_SAMPLE_FORMAT_GET_CHANNELS(). */ typedef enum { IPATCH_SAMPLE_MONO = 0 << IPATCH_SAMPLE_CHANNEL_SHIFT, IPATCH_SAMPLE_STEREO = 1 << IPATCH_SAMPLE_CHANNEL_SHIFT } IpatchSampleChannel; /** * IpatchSampleChannelType: * @IPATCH_SAMPLE_LEFT: Left channel comes first * @IPATCH_SAMPLE_RIGHT: Right channel comes second * * Channel designation. Currently there are only 2 designated channels, * though the remaining 6 supported channels may be defined to be surround * sound channels in the future. */ typedef enum { IPATCH_SAMPLE_LEFT = 0, IPATCH_SAMPLE_RIGHT = 1 } IpatchSampleChannelType; /** * IPATCH_SAMPLE_MAX_CHANNELS: * * Maximum number of audio channels handled by libInstPatch. */ #define IPATCH_SAMPLE_MAX_CHANNELS 8 /** * IpatchSampleSign: * @IPATCH_SAMPLE_SIGNED: Signed PCM audio data. * @IPATCH_SAMPLE_UNSIGNED: Unsigned PCM audio data. * * Defines the sign of PCM integer audio data. */ typedef enum { IPATCH_SAMPLE_SIGNED = 0 << IPATCH_SAMPLE_SIGN_SHIFT, IPATCH_SAMPLE_UNSIGNED = 1 << IPATCH_SAMPLE_SIGN_SHIFT } IpatchSampleSign; /** * IpatchSampleEndian: * @IPATCH_SAMPLE_LENDIAN: Little endian byte order * @IPATCH_SAMPLE_BENDIAN: Big endian byte order * * Defines the byte order of multi-byte audio data. */ typedef enum { IPATCH_SAMPLE_LENDIAN = 0 << IPATCH_SAMPLE_ENDIAN_SHIFT, IPATCH_SAMPLE_BENDIAN = 1 << IPATCH_SAMPLE_ENDIAN_SHIFT } IpatchSampleEndian; /** * IPATCH_SAMPLE_ENDIAN_HOST: * * Host byte order value (#IPATCH_SAMPLE_LENDIAN or #IPATCH_SAMPLE_BENDIAN). */ #if G_BYTE_ORDER == G_LITTLE_ENDIAN #define IPATCH_SAMPLE_ENDIAN_HOST IPATCH_SAMPLE_LENDIAN #else #define IPATCH_SAMPLE_ENDIAN_HOST IPATCH_SAMPLE_BENDIAN #endif /** * IPATCH_SAMPLE_FORMAT_GET_WIDTH: * @format: Sample format integer * * Get #IpatchSampleWidth enum from a sample format integer. * * Returns: Format field of sample format integer. */ #define IPATCH_SAMPLE_FORMAT_GET_WIDTH(format) \ ((format) & IPATCH_SAMPLE_WIDTH_MASK) /** * IPATCH_SAMPLE_FORMAT_IS_FLOATING: * @format: Sample format integer * * Check if a sample format integer defines floating point audio. * * Returns: %TRUE if sample format is #IPATCH_SAMPLE_FLOAT or #IPATCH_SAMPLE_DOUBLE. */ #define IPATCH_SAMPLE_FORMAT_IS_FLOATING(format) \ (((format) & IPATCH_SAMPLE_WIDTH_MASK) == IPATCH_SAMPLE_FLOAT \ || ((format) & IPATCH_SAMPLE_WIDTH_MASK) == IPATCH_SAMPLE_DOUBLE) /** * IPATCH_SAMPLE_FORMAT_GET_CHANNELS: * @format: Sample format integer * * Get the channel field from a sample format integer. * * Returns: Channel field value (see #IpatchSampleChannel) */ #define IPATCH_SAMPLE_FORMAT_GET_CHANNELS(format) \ ((format) & IPATCH_SAMPLE_CHANNEL_MASK) /** * IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT: * @format: Sample format integer * * Get the channel count from a sample format integer. * * Returns: Channel count (starting at 1 for mono). */ #define IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format) \ ((((format) & IPATCH_SAMPLE_CHANNEL_MASK) >> IPATCH_SAMPLE_CHANNEL_SHIFT) + 1) /** * IPATCH_SAMPLE_FORMAT_IS_SIGNED: * @format: Sample format integer * * Check if a sample format integer defines signed audio. * * Returns: %TRUE if sample format is signed. */ #define IPATCH_SAMPLE_FORMAT_IS_SIGNED(format) \ (((format) & IPATCH_SAMPLE_UNSIGNED) == 0) /** * IPATCH_SAMPLE_FORMAT_IS_UNSIGNED: * @format: Sample format integer * * Check if a sample format integer defines unsigned audio. * * Returns: %TRUE if sample format is unsigned. */ #define IPATCH_SAMPLE_FORMAT_IS_UNSIGNED(format) \ (((format) & IPATCH_SAMPLE_UNSIGNED) != 0) /** * IPATCH_SAMPLE_FORMAT_IS_LENDIAN: * @format: Sample format integer * * Check if a sample format integer defines little endian audio. * * Returns: %TRUE if sample format is little endian. */ #define IPATCH_SAMPLE_FORMAT_IS_LENDIAN(format) \ (((format) & IPATCH_SAMPLE_BENDIAN) == 0) /** * IPATCH_SAMPLE_FORMAT_IS_BENDIAN: * @format: Sample format integer * * Check if a sample format integer defines big endian audio. * * Returns: %TRUE if sample format is big endian. */ #define IPATCH_SAMPLE_FORMAT_IS_BENDIAN(format) \ (((format) & IPATCH_SAMPLE_BENDIAN) != 0) /** * ipatch_sample_format_size: * @format: Sample format integer * * Get frame byte size for a given sample format (sample byte size * channels). * * Returns: Size in bytes of a single sample frame. */ #define ipatch_sample_format_size(format) \ (ipatch_sample_width_sizes[(format) & IPATCH_SAMPLE_WIDTH_MASK] \ * IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT (format)) /** * ipatch_sample_format_width: * @format: Sample format * * Gets the number of bytes used for storing a single sample for @format. * Doesn't take into account channels. This is the number of bytes used * to store the samples, not the effective bit width. For example: * #IPATCH_SAMPLE_24BIT uses 4 bytes for each sample. * * Returns: Byte width of a single sample (not including channels). */ #define ipatch_sample_format_width(format) \ (ipatch_sample_width_sizes[(format) & IPATCH_SAMPLE_WIDTH_MASK]) /** * IPATCH_SAMPLE_MAP_CHANNEL: * @dest: Destination channel in mapping bit field (0-7) * @src: Source channel (0-7) * * Macro to calculate a channel mapping value for a given destination and * source. A channel mapping is composed of up to 24 bits * (3 bits * 8 channels = 24). Channel mappings are used for sample * conversions to route channels from a source format to a destination format. * Multiple channel map values should be OR'd together. */ #define IPATCH_SAMPLE_MAP_CHANNEL(dest, src) ((src) << (3 * (dest))) /** * IPATCH_SAMPLE_MAP_GET_CHANNEL: * @map: Channel map value (#guint32 - only 24 bits are used) * @dest: Destination channel in @map bit field (0-7) * * Macro to get a source channel value given a destination channel. * * Returns: Source channel for @dest (0-7) */ #define IPATCH_SAMPLE_MAP_GET_CHANNEL(map, dest) (((map) >> ((dest) * 3)) & 0x07) /** * IPATCH_SAMPLE_UNITY_CHANNEL_MAP: * * Unity channel mapping which routes each input channel to the same output * channel. */ #define IPATCH_SAMPLE_UNITY_CHANNEL_MAP 0xFAC688 extern guint ipatch_sample_width_sizes[16]; int ipatch_sample_format_bit_width(int format); gboolean ipatch_sample_format_verify(int format); gboolean ipatch_sample_format_transform_verify(int src_format, int dest_format, guint32 channel_map); guint ipatch_sample_get_transform_funcs(int src_format, int dest_format, guint32 channel_map, guint *buf1_max_frame, guint *buf2_max_frame, IpatchSampleTransformFunc *funcs); #endif libinstpatch-1.1.6/libinstpatch/util.c000066400000000000000000000152451400263525300200170ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ /** * SECTION: util * @short_description: Utility functions * @see_also: * @stability: Stable */ #include #include #include #include #include "util.h" #include "misc.h" #include "compat.h" #include "i18n.h" /* Convenience boolean GValue constants */ GValue *ipatch_util_value_bool_true; GValue *ipatch_util_value_bool_false; /** * _ipatch_util_init: (skip) */ void _ipatch_util_init(void) { ipatch_util_value_bool_true = g_new0(GValue, 1); ipatch_util_value_bool_false = g_new0(GValue, 1); g_value_init(ipatch_util_value_bool_true, G_TYPE_BOOLEAN); g_value_init(ipatch_util_value_bool_false, G_TYPE_BOOLEAN); g_value_set_boolean(ipatch_util_value_bool_true, TRUE); g_value_set_boolean(ipatch_util_value_bool_false, FALSE); } /** * _ipatch_util_deinit * * Free GValue values */ void _ipatch_util_deinit(void) { g_value_unset(ipatch_util_value_bool_true); g_value_unset(ipatch_util_value_bool_false); g_free(ipatch_util_value_bool_true); g_free(ipatch_util_value_bool_false); } /** * ipatch_util_value_hash: * @val: GValue to hash * * Hash a GValue. The hash value can then be used in a GHashTable for example. * * Returns: Hash value corresponding to the @val key. */ guint ipatch_util_value_hash(GValue *val) { GValueArray *valarray; GType valtype; const char *sval; /* so we can access a float value as its raw data without gcc bitching */ union { gfloat f; guint32 i; } fval; g_return_val_if_fail(G_IS_VALUE(val), 0); valtype = G_VALUE_TYPE(val); switch(G_TYPE_FUNDAMENTAL(valtype)) { case G_TYPE_CHAR: return (g_value_get_char(val)); case G_TYPE_UCHAR: return (g_value_get_uchar(val)); case G_TYPE_BOOLEAN: return (g_value_get_boolean(val)); case G_TYPE_INT: return (g_value_get_int(val)); case G_TYPE_UINT: return (g_value_get_uint(val)); case G_TYPE_LONG: return (g_value_get_long(val)); case G_TYPE_ULONG: return (g_value_get_ulong(val)); case G_TYPE_INT64: return (guint)(g_value_get_int64(val)); case G_TYPE_UINT64: return (guint)(g_value_get_uint64(val)); case G_TYPE_ENUM: return (g_value_get_enum(val)); case G_TYPE_FLAGS: return (g_value_get_flags(val)); case G_TYPE_FLOAT: fval.f = g_value_get_float(val); return (fval.i); /* use the raw float data as hash */ case G_TYPE_DOUBLE: fval.f = (gfloat)g_value_get_double(val); /* convert double to float */ return (fval.i); /* use the raw float data as hash */ case G_TYPE_STRING: sval = g_value_get_string(val); return (sval ? g_str_hash(sval) : 0); case G_TYPE_POINTER: return (GPOINTER_TO_UINT(g_value_get_pointer(val))); case G_TYPE_BOXED: if(valtype == G_TYPE_VALUE_ARRAY) /* value array? */ { valarray = g_value_get_boxed(val); return (ipatch_util_value_array_hash(valarray)); } else { return (GPOINTER_TO_UINT(g_value_get_boxed(val))); } case G_TYPE_PARAM: return (GPOINTER_TO_UINT(g_value_get_param(val))); case G_TYPE_OBJECT: return (GPOINTER_TO_UINT(g_value_get_object(val))); default: g_assert_not_reached(); return (0); /* to keep compiler happy */ } } /** * ipatch_util_value_array_hash: * @valarray: GValueArray to hash * * Hash a GValueArray. The hash value can then be used in a GHashTable for * example. * * Returns: Hash value corresponding to the sum of all values returned * by ipatch_util_value_hash() for each GValue in the array. */ guint ipatch_util_value_array_hash(GValueArray *valarray) { GValue *value; guint hashval = 0; guint i; if(!valarray) { return (0); } for(i = 0; i < valarray->n_values; i++) { value = g_value_array_get_nth(valarray, i); hashval += ipatch_util_value_hash(value); } return (hashval); } /** * ipatch_util_file_size: (skip) * @fname: Path of file to get size of. * @err: Location to store error or %NULL * * Get the size of a file (tired of using stat every time?). * * Returns: File size. Will return 0 on error, but @err must be checked if * it is set to determine if an error really occurred. */ guint64 ipatch_util_file_size(const char *fname, GError **err) { GStatBuf st; g_return_val_if_fail(fname != NULL, 0); g_return_val_if_fail(!err || !*err, 0); if(g_stat(fname, &st) != 0) { g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_IO, _("Error stating file '%s': %s"), fname, g_strerror(errno)); return (0); } return (st.st_size); } /** * ipatch_util_abs_filename: (skip) * @filename: File name to make absolute * * Make a file name absolute, if it isn't already. * * Returns: Newly allocated filename, converted to an absolute filename (if necessary) * or NULL if @filename was NULL * * Since: 1.1.0 */ char * ipatch_util_abs_filename(const char *filename) { char *dirname, *abs_filename; if(!filename) { return (NULL); } if(g_path_is_absolute(filename)) { return (g_strdup(filename)); // !! caller takes over allocation } dirname = g_get_current_dir(); // ++ alloc abs_filename = g_build_filename(dirname, filename, NULL); g_free(dirname); // -- free return (abs_filename); // !! caller takes over allocation } /** * ipatch_util_weakref_destroy: (skip) * @value: Slice allocated GWeakRef to destroy * * A GDestroyNotify function for freeing a slice allocated * GWeakRef. * * Since: 1.1.0 */ void ipatch_util_weakref_destroy(gpointer value) { GWeakRef *weakref = value; g_weak_ref_clear(weakref); g_slice_free(GWeakRef, weakref); } libinstpatch-1.1.6/libinstpatch/util.h000066400000000000000000000027331400263525300200220ustar00rootroot00000000000000/* * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_UTIL_H__ #define __IPATCH_UTIL_H__ #include #include extern GValue *ipatch_util_value_bool_true; extern GValue *ipatch_util_value_bool_false; /* a pointer to a constant boolean GValue for TRUE or FALSE depending on input * value. */ #define IPATCH_UTIL_VALUE_BOOL(b) \ ((b) ? ipatch_util_value_bool_true : ipatch_util_value_bool_false) guint ipatch_util_value_hash(GValue *val); guint ipatch_util_value_array_hash(GValueArray *valarray); guint64 ipatch_util_file_size(const char *fname, GError **err); char *ipatch_util_abs_filename(const char *filename); void ipatch_util_weakref_destroy(gpointer value); #endif libinstpatch-1.1.6/libinstpatch/version.h.in000066400000000000000000000030271400263525300211340ustar00rootroot00000000000000/* * version.h - libInstPatch version information * * libInstPatch * Copyright (C) 1999-2002 Josh Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #ifndef __IPATCH_VERSION_H__ #define __IPATCH_VERSION_H__ /** * IPATCH_VERSION: (skip) * libInstPatch version static string. */ #define IPATCH_VERSION @IPATCH_VERSION@ /** * IPATCH_VERSION_MAJOR: (skip) * libInstPatch major version integer. */ #define IPATCH_VERSION_MAJOR @IPATCH_VERSION_MAJOR@ /** * IPATCH_VERSION_MINOR: (skip) * libInstPatch minor version integer. */ #define IPATCH_VERSION_MINOR @IPATCH_VERSION_MINOR@ /** * IPATCH_VERSION_MICRO: (skip) * libInstPatch micro version integer. */ #define IPATCH_VERSION_MICRO @IPATCH_VERSION_MICRO@ void ipatch_version (guint *major, guint *minor, guint *micro); #endif /* __IPATCH_VERSION_H__ */ libinstpatch-1.1.6/tests/000077500000000000000000000000001400263525300153455ustar00rootroot00000000000000libinstpatch-1.1.6/tests/Converter.py000077500000000000000000000056701400263525300177010ustar00rootroot00000000000000#!/usr/bin/python # # Project: lib Instrument Patch Python testing suite # File: Converter.py # Descr: Object converter tests # Author: Element Green # Date: 2016-07-04 # License: Public Domain # import Test import gi from gi.repository import GObject gi.require_version ('Ipatch', '1.1') from gi.repository import Ipatch # Common converter source to destination type mappings CONVERTER_TYPES = ( ("IpatchFile", "IpatchBase"), ("IpatchBase", "IpatchFile"), ("IpatchItem", "IpatchSF2VoiceCache"), ("IpatchSndFile", "IpatchItem") ) # Main if __name__ == "__main__": parser = Test.createArgParser ('libInstPatch Converter tests') args = Test.parseArgs (parser) Ipatch.init () convinfo = [Ipatch.get_converter_info (t) for t in Ipatch.find_converters (GObject.TYPE_NONE, GObject.TYPE_NONE, 0)] convinfo.sort (key=lambda info: GObject.type_name (info.conv_type)) if len (convinfo) == 0: Test.error ("No converters found!") Test.msg ("Found %d converters\n" % len (convinfo)) summaryCounts = [0] * len (CONVERTER_TYPES) summaryOther = 0 # Show converter details for convindex in xrange (0, len (convinfo)): conv = convinfo[convindex] s = GObject.type_name (conv.conv_type) + "\n" if conv.src_count == Ipatch.ConverterCount.ONE_OR_MORE: count = '+' elif conv.src_count == Ipatch.ConverterCount.ZERO_OR_MORE: count = '*' else: count = str (conv.src_count) if conv.src_match == GObject.TYPE_INVALID: s += " src[%s]=%s " % (count, GObject.type_name (conv.src_type)) else: s += " src[%s]=%s - %s" \ % (count, GObject.type_name (conv.src_match), GObject.type_name (conv.src_type)) if conv.flags & Ipatch.ConverterFlags.SRC_DERIVED: s += "+ DESCENDANTS\n" else: s += "\n" if conv.dest_count == Ipatch.ConverterCount.ONE_OR_MORE: count = '+' elif conv.dest_count == Ipatch.ConverterCount.ZERO_OR_MORE: count = '*' else: count = str (conv.dest_count) if conv.dest_match == GObject.TYPE_INVALID: s += " dst[%s]=%s" % (count, GObject.type_name (conv.dest_type)) else: s += " dst[%s]=%s - %s" \ % (count, GObject.type_name (conv.dest_match), GObject.type_name (conv.dest_type)) if conv.flags & Ipatch.ConverterFlags.DEST_DERIVED: s += "+ DESCENDANTS" Test.info (s) for i in xrange (0, len (CONVERTER_TYPES)): convTypes = CONVERTER_TYPES[i] if GObject.type_is_a (conv.src_type, GObject.type_name (convTypes[0])) \ and GObject.type_is_a (conv.dest_type, GObject.type_name (convTypes[1])): summaryCounts[i] += 1 break else: summaryOther += 1 Test.info ("") Test.msg ("Converter type summary:") for i in xrange (0, len (CONVERTER_TYPES)): convTypes = CONVERTER_TYPES[i] Test.msg ("%s -> %s: %d" % (convTypes[0], convTypes[1], summaryCounts[i])) if summaryOther > 0: Test.msg ("Other: %d" % (convTypes[0], convTypes[1], summaryCounts[i])) Test.exit () libinstpatch-1.1.6/tests/SF2.py000077500000000000000000000174441400263525300163260ustar00rootroot00000000000000#!/usr/bin/python # # Project: lib Instrument Patch Python testing suite # File: SF2.py # Descr: SoundFont tests # Author: Element Green # Date: 2016-07-01 # License: Public Domain # import Test import gi import ctypes import math import sys import os from gi.repository import GObject gi.require_version ('Ipatch', '1.1') from gi.repository import Ipatch # Sample audio format (native SoundFont 16 bit mono signed little endian byte order audio) SAMPLE_FORMAT = Ipatch.SampleWidth.BIT16 | Ipatch.SampleChannel.MONO | Ipatch.SampleSign.SIGNED | Ipatch.SampleEndian.LENDIAN # Format used for writing sample data (converted from this format) ACCESS_FORMAT = Ipatch.SampleWidth.FLOAT | Ipatch.SampleChannel.MONO | Ipatch.SampleSign.SIGNED SAMPLE_RATE = 48000 # Sample rate SAMPLE_LOOP_PADDING = 4 # Number of samples before and after the sample loop TEST_SF2_FILENAME = os.path.join (Test.TEST_DATA_PATH, 'test.sf2') # Set sample access format endian byte order depending on host system ACCESS_FORMAT |= Ipatch.SampleEndian.BENDIAN if sys.byteorder == 'big' else Ipatch.SampleEndian.LENDIAN def SineWave (periodPos): """Sine waveform generation function""" return math.sin (periodPos * 2 * math.pi) def SawWave (periodPos): """Triangle waveform generation function""" if (periodPos <= 0.25): return periodPos / 0.25 elif (periodPos <= 0.75): return (0.75 - periodPos) * 4.0 - 1.0 else: return (1.0 - periodPos) * -4.0 def SquareWave (periodPos): """Square waveform generation function""" return 1.0 if periodPos <= 0.5 else 0.0 def sampleDataGen (freq, mathfunc): """Raw sample data generator""" period = float (SAMPLE_RATE) / freq start = int (math.floor (period - SAMPLE_LOOP_PADDING)) end = int (math.ceil (2 * period + SAMPLE_LOOP_PADDING)) size = end - start buf = (ctypes.c_float * size)() # Generate a single period of the waveform, including loop padding for i in xrange (0, size): periodPos = math.fmod (start + i, period) / period buf[i] = mathfunc (periodPos) return bytearray (buf) def createSF2Sample (floatData): """Generate sample data object""" sampleSize = len (floatData) / 4 # Create RAM sample store then set format, rate, and size store = Ipatch.SampleStoreRam () store.set_properties (sample_format=SAMPLE_FORMAT, sample_rate=SAMPLE_RATE, sample_size=sampleSize) # Open a handle to the sample store and write the waveform floating point data to it retval, handle = store.handle_open ('w', ACCESS_FORMAT, 0) handle.write (0, floatData) # 0 is the position to write to (start of sample) handle.close () # Create sample data object and add sample store to it sampleData = Ipatch.SampleData () sampleData.add (store) # Create SF2Sample and add sample data to it sf2Sample = Ipatch.SF2Sample () sf2Sample.set_data (sampleData) loopStart = SAMPLE_LOOP_PADDING loopEnd = sampleSize - SAMPLE_LOOP_PADDING - 1 # Calculate root note and fine tune based on loop length for a single waveform notecents = Ipatch.unit_hertz_to_cents (float (SAMPLE_RATE) / (loopEnd - loopStart)) cents = math.fmod (notecents, 100.0) # Fine tune offset corrects for inaccuracy of a single digital waveform if cents <= 50.0: rootNote = int (notecents / 100.0) fineTune = -int (round (cents)) else: rootNote = int (notecents / 100.0) + 1 fineTune = int (round (100.0 - cents)) sf2Sample.set_properties (sample_rate=SAMPLE_RATE, loop_start=loopStart, loop_end=loopEnd, root_note=rootNote, fine_tune=fineTune) return sf2Sample def addSF2Preset (sf2, name, midiProgram, sampleMathFunc): """Create a SoundFont preset, with an instrument, and containing several generated samples""" # Create a SF2 preset object, set its MIDI bank and program numbers (MIDI locale), and add it to the SF2 object preset = Ipatch.SF2Preset () preset.set_name (name) preset.set_midi_locale (0, midiProgram) sf2.add (preset) # Create a SF2 instrument object and add it to the SF2 object inst = Ipatch.SF2Inst () inst.set_name (name) sf2.add (inst) preset.new_zone (inst) # Create new preset zone and add instrument to it # Create 2 modulators to disable the default pitch modulation control and modulate the filter cutoff instead mod = Ipatch.SF2Mod () mod.src = 1 | Ipatch.SF2ModControlPalette.MIDI | Ipatch.SF2ModDirection.POSITIVE \ | Ipatch.SF2ModPolarity.UNIPOLAR | Ipatch.SF2ModType.LINEAR mod.dest = Ipatch.SF2GenType.VIB_LFO_TO_PITCH mod.amount = 0 mod.amtsrc = 0 mod.trans = Ipatch.SF2ModTransform.LINEAR modlist = [mod] # Create modulator list mod = Ipatch.SF2Mod () mod.src = 1 | Ipatch.SF2ModControlPalette.MIDI | Ipatch.SF2ModDirection.POSITIVE \ | Ipatch.SF2ModPolarity.UNIPOLAR | Ipatch.SF2ModType.LINEAR mod.dest = Ipatch.SF2GenType.MOD_LFO_TO_FILTER_CUTOFF mod.amount = 2400 mod.amtsrc = 0 mod.trans = Ipatch.SF2ModTransform.LINEAR modlist.append (mod) # Append 2nd modulator to list # Set instrument global modulators to modulator list inst.set_mods (modlist) # Create A note samples for octaves A0 through A6 (7 octaves) and add to instrument zones for octave in xrange (0, 7): note = 33 + octave * 12 # MIDI note 33 is A0 sample = createSF2Sample (sampleDataGen (Ipatch.unit_cents_to_hertz (note * 100), sampleMathFunc)) sample.set_name ('%s A%d' % (name, octave)) sf2.append (sample) # Add sample to SF2 base object # Create instrument zone izone = Ipatch.SF2IZone () inst.add (izone) izone.set_sample (sample) # Assign the sample to the zone # Calculate optimal note ranges for samples start = note - 5 if octave > 0 else 0 end = note + 6 if octave < 6 else 127 izone.set_properties (loop_type=Ipatch.SampleLoopType.STANDARD, # Enable looping note_range=Ipatch.Range.new (start, end)) # Create range and assign to note range def createSF2 (): """Create SoundFont object""" # Create SoundFont object, a file object, and assign it sf2 = Ipatch.SF2 () sf2.props.name = "Test SoundFont" sf2.props.author = "Robo Python 3000+" sf2.props.comment = """This is a test SoundFont generated by the Instrument Patch library Python binding. One should find 3 different presets for Sine, Saw, and Square waves with 7 samples each. Enjoy!""" sf2File = Ipatch.SF2File () sf2File.set_name (TEST_SF2_FILENAME) sf2.set_file (sf2File) # Add presets for sine, saw, and square waves addSF2Preset (sf2, "Sine", 0, SineWave) addSF2Preset (sf2, "Saw", 1, SawWave) addSF2Preset (sf2, "Square", 2, SquareWave) return sf2 # Main if __name__ == "__main__": parser = Test.createArgParser ('libInstPatch SoundFont tests') parser.add_argument ('-n', '--nodelete', action='store_true', help="Don't delete created test SoundFont (%s)" % TEST_SF2_FILENAME) args = Test.parseArgs (parser) Ipatch.init () Test.msg ("Creating SoundFont") sf2 = createSF2 () Test.msg ("Saving SoundFont") sf2.save () Test.msg ("Loading SoundFont") sf2file = Ipatch.SF2File () sf2file.set_property ('file-name', TEST_SF2_FILENAME) cmpsf2 = Ipatch.convert_object_to_type (sf2file, Ipatch.SF2) blacklistProps = { (Ipatch.SF2, "software"), # SF2 software will include the loaded software version appended (Ipatch.SF2File, "sample-size") # Sample size is the total sample data in the SoundFont file which will differ } # Clear changed and saved flags so they don't trigger a value mismatch sf2.clear_flags (Ipatch.BaseFlags.CHANGED | Ipatch.BaseFlags.SAVED) cmpsf2.clear_flags (Ipatch.BaseFlags.CHANGED | Ipatch.BaseFlags.SAVED) Test.msg ("Verifying SoundFont") Test.compareItems (sf2, cmpsf2, blackList=blacklistProps) if not args.nodelete: os.unlink (TEST_SF2_FILENAME) Test.exit () libinstpatch-1.1.6/tests/SampleTransform.py000077500000000000000000000133501400263525300210410ustar00rootroot00000000000000#!/usr/bin/python # # Project: lib Instrument Patch Python testing suite # File: SF2.py # Descr: SoundFont tests # Author: Element Green # Date: 2016-07-01 # License: Public Domain # """ Convert between all possible audio format pairs using the following steps: - Convert double to first format of format pair - Convert from first format to second format - Convert second format back to double - Calculate maximum audio sample difference between new double audio and original """ import Test import gi import ctypes import math import sys from gi.repository import GObject gi.require_version ('Ipatch', '1.1') from gi.repository import Ipatch # Default values are non powers of 2 to decrease the chance of masking periodic data issues DEFAULT_SAMPLE_SIZE = 1756 # default test waveform size in samples DEFAULT_SAMPLE_PERIOD = 123 # default waveform period in samples DEFAULT_TRANSFORM_SIZE = 65000 # default transform buffer size in bytes MAX_DIFF_ALLOWED = 0.016 # maximum difference allowed # all available sample format combinations testFormats = ( Ipatch.SampleWidth.BIT8, Ipatch.SampleWidth.BIT16, Ipatch.SampleWidth.BIT24, Ipatch.SampleWidth.BIT32, Ipatch.SampleWidth.FLOAT, Ipatch.SampleWidth.DOUBLE, Ipatch.SampleWidth.REAL24BIT, Ipatch.SampleWidth.BIT8 | Ipatch.SampleSign.UNSIGNED, Ipatch.SampleWidth.BIT16 | Ipatch.SampleSign.UNSIGNED, Ipatch.SampleWidth.BIT24 | Ipatch.SampleSign.UNSIGNED, Ipatch.SampleWidth.BIT32 | Ipatch.SampleSign.UNSIGNED, Ipatch.SampleWidth.REAL24BIT | Ipatch.SampleSign.UNSIGNED, Ipatch.SampleWidth.BIT16 | Ipatch.SampleEndian.BENDIAN, Ipatch.SampleWidth.BIT24 | Ipatch.SampleEndian.BENDIAN, Ipatch.SampleWidth.BIT32 | Ipatch.SampleEndian.BENDIAN, Ipatch.SampleWidth.FLOAT | Ipatch.SampleEndian.BENDIAN, Ipatch.SampleWidth.DOUBLE | Ipatch.SampleEndian.BENDIAN, Ipatch.SampleWidth.REAL24BIT | Ipatch.SampleEndian.BENDIAN, Ipatch.SampleWidth.BIT16 | Ipatch.SampleSign.UNSIGNED | Ipatch.SampleEndian.BENDIAN, Ipatch.SampleWidth.BIT24 | Ipatch.SampleSign.UNSIGNED | Ipatch.SampleEndian.BENDIAN, Ipatch.SampleWidth.BIT32 | Ipatch.SampleSign.UNSIGNED | Ipatch.SampleEndian.BENDIAN, Ipatch.SampleWidth.REAL24BIT | Ipatch.SampleSign.UNSIGNED | Ipatch.SampleEndian.BENDIAN ) testFormatNames = ( "8bit-signed", "16bit-signed-lendian", "24bit-signed-lendian", "32bit-signed-lendian", "float-lendian", "double-lendian", "real24bit-signed-lendian", "8bit-unsigned", "16bit-unsigned-lendian", "24bit-unsigned-lendian", "32bit-unsigned-lendian", "real24bit-unsigned-lendian", "16bit-signed-bendian", "24bit-signed-bendian", "32bit-signed-bendian", "float-bendian", "double-bendian", "real24bit-signed-bendian", "16bit-unsigned-bendian", "24bit-unsigned-bendian", "32bit-unsigned-bendian", "real24bit-unsigned-bendian" ) parser = Test.createArgParser ('libInstPatch audio sample transform tests') parser.add_argument ( '-s', '--size', type=int, default=DEFAULT_SAMPLE_SIZE, help='Size of audio to transform in frames') parser.add_argument ('-p', '--period', type=int, default=DEFAULT_SAMPLE_PERIOD, help='Period size in frames') parser.add_argument ('-t', '--trans', type=int, default=DEFAULT_TRANSFORM_SIZE, help='Transform size in bytes') args = Test.parseArgs (parser) Ipatch.init () # Determine host endian byte order hostEndian = Ipatch.SampleEndian.BENDIAN if sys.byteorder == 'big' else Ipatch.SampleEndian.LENDIAN # Create double floating point sample data audio sampleSize = args.size period = args.period sampleDataArray = (ctypes.c_double * sampleSize)() for pos in xrange (0, sampleSize): periodPos = math.fmod (pos, period) / period sampleDataArray[pos] = math.sin (periodPos * 2 * math.pi) sampleData = bytearray (sampleDataArray) # Create sample transform object and initialize size trans = Ipatch.SampleTransform () trans.alloc_size (args.trans) failcount = 0 max_maxdiff = 0.0 for isrc in xrange (0, len (testFormats)): srcform = testFormats[isrc] for idest in xrange (0, len (testFormats)): destform = testFormats[idest] # convert generated double floating point waveform to source format trans.set_formats (Ipatch.SampleWidth.DOUBLE | hostEndian, srcform, Ipatch.SAMPLE_UNITY_CHANNEL_MAP) srcData = trans.convert (sampleData) # convert source format to destination format trans.set_formats (srcform, destform, Ipatch.SAMPLE_UNITY_CHANNEL_MAP) destData = trans.convert (srcData) # convert destination format to final double output trans.set_formats (destform, Ipatch.SampleWidth.DOUBLE | hostEndian, Ipatch.SAMPLE_UNITY_CHANNEL_MAP) finalData = trans.convert (destData) finalDataArray = (ctypes.c_double * sampleSize).from_buffer_copy (finalData) maxdiff = 0.0 maxindex = 0 # compare final waveform against original for i in xrange (0, sampleSize): d = abs (sampleDataArray[i] - finalDataArray[i]) if d > maxdiff: maxdiff = d maxindex = i if maxdiff > max_maxdiff: max_maxdiff = maxdiff if maxdiff <= MAX_DIFF_ALLOWED: Test.info ("Converted %s to %s: maxdiff=%0.6f, index=%d" % (testFormatNames[isrc], testFormatNames[idest], maxdiff, maxindex)) else: Test.error ("FAILED Converting %s to %s: maxdiff=%0.6f, index=%d" % (testFormatNames[isrc], testFormatNames[idest], maxdiff, maxindex)) failcount += 1 if failcount > 0: Test.error ("%d of %d format conversions FAILED (max diff=%0.6f)" % (failcount, len (testFormats) ** 2, max_maxdiff)) else: Test.msg ("All %d sample format conversions PASSED (max diff=%0.6f)" % (len (testFormats) ** 2, max_maxdiff)) Test.exit () libinstpatch-1.1.6/tests/TODO000066400000000000000000000030561400263525300160410ustar00rootroot00000000000000Testing using Python GObject Introspection binding DONE ====================================== Error detection: * Hook glib logv messages with non success exit value IpatchConverter * Converter introspection IpatchSampleTransform * Transform tests IpatchSF2 * Create, Save, Load, Compare * Generators * Modulators TODO ====================================== Error detection: * Memory leaks * GObject reference leaks IpatchBase * CHANGED flag set when children changed IpatchContainer * Traverse tree * Insert, append, prepend * Remove * Count * make_unique, add_unique * Remove/add notifies IpatchConverter * Test conversions * Convert link lookup/notify IpatchFile * File pool * File references * Rename, unlink, replace * Read/write/seek * File identify IpatchItem * Property notifies * Copy objects * Duplicate objects * Unique properties IpatchItemProp * List * Set/get/verify IpatchPaste * Paste handler introspection * Paste testing * Paste choices IpatchSampleData * Data migration * Used/unused counting * Sample cache IpatchSampleList * Append, prepend, insert, cut, verify IpatchSampleStore* * File, RAM, ROM, SndFile, Split24, Swap, Virtual IpatchSF2VoiceCache * Convert synthesis items * Select and update operations IpatchSndFile * Read/write/verify IpatchState * Item type introspection * Groups * Record * Retract * Undo/Redo IpatchTypeProp * List * Set/get/verify IpatchUnit * Unit type introspection * Conversion introspection * Conversion testing IpatchVBank * Create/test libinstpatch-1.1.6/tests/Test.py000066400000000000000000000106561400263525300166460ustar00rootroot00000000000000#!/usr/bin/python # # Project: lib Instrument Patch Python testing suite # File: Testing.py # Descr: Testing support functions used by other tests # Author: Element Green # Date: 2016-07-01 # License: Public Domain # import sys import os import argparse import gi from gi.repository import GLib from gi.repository import GObject gi.require_version ('Ipatch', '1.1') from gi.repository import Ipatch ErrorCount = 0 LogMask = 0 Verbose = 1 TEST_DATA_PATH = "test-data/" def createArgParser (description): parser = argparse.ArgumentParser (description=description) parser.add_argument ('-v', '--verbose', action='count', help='Increase verbosity') parser.add_argument ('--quiet', action='store_true', help='Only display errors') return parser def parseArgs (parser): global Verbose args = parser.parse_args () if args.verbose > 0: Verbose = args.verbose + 1 if args.quiet: Verbose = 0 return args def LogTrap (log_domain, log_level, message, user_data): global LogMask LogMask |= log_level GLib.log_default_handler ("Testing", log_level, message, 0) def exit (): if LogMask: print ("GLib warning/error messages occurred!") if ErrorCount > 0: print ("%d Errors occurred!" % ErrorCount) sys.exit (0 if ErrorCount == 0 and LogMask == 0 else 1) def info (s): if Verbose >= 2: print (s) def msg (s): if Verbose >= 1: print (s) def error (s): global ErrorCount print (s) ErrorCount += 1 def compareItems (item1, item2, recurse=True, blackList=()): """Compare two Ipatch.Item objects""" if type (item1) != type (item2): raise RuntimeError ('Item type %s != %s' % (str (type (item1)), str (type (item2)))) typename = str (type (item1)) props = sorted ([prop for prop in item1.list_properties ()], key=lambda prop: prop.name) blprops = [propname for typ, propname in blackList if isinstance (item1, typ)] for p in props: if p.name in blprops: continue val1 = item1.get_property (p.name) val2 = item2.get_property (p.name) valtype = p.value_type if val1 is None != val2 is None: raise RuntimeError ('Property value None mismatch for property %s:%s' % (typename, p.name)) if valtype.is_a (GObject.Object): if val1 is not None and recurse: compareItems (val1, val2, recurse=False, blackList=blackList) # Don't recurse into object fields (causes recursion loops) elif valtype.is_a (Ipatch.Range): if val1.low != val2.low or val1.high != val2.high: RuntimeError ('Range value mismatch for property %s:%s, %d-%d != %d-%d' % (typename, p.name, val1.low, val1.high, val2.low, val2.high)) elif valtype.is_a (GObject.GBoxed): if valtype.name == 'IpatchSF2ModList': if len (val1) != len (val2): RuntimeError ('Modulator list length mismatch for property %s:%s, %d != %d' % (typename, p.name, len (val1), len (val2))) for i in xrange (0, len (val1)): v1 = val1[i] v2 = val2[i] v1 = (v1.src, v1.amtsrc, v1.dest, v1.amount, v1.trans) v2 = (v2.src, v2.amtsrc, v2.dest, v2.amount, v2.trans) if v1 != v2: RuntimeError ('Modulator value mismatch for property %s:%s, %s != %s' % (typename, p.name, str (v1), str (v2))) else: print ('Ignoring property %s:%s of type %s' % (typename, p.name, str (valtype))) elif val1 != val2: raise RuntimeError ('Value mismatch for property %s:%s, %s != %s' % (typename, p.name, str (val1), str (val2))) if recurse and isinstance (item1, Ipatch.Container): children1 = item1.get_children () children2 = item2.get_children () if len (children1) != len (children2): raise RuntimeError ('Container of type %s has unequal children count, %d != %d' % (typename, len (children1), len (children2))) for i in xrange (0, len (children1)): compareItems (children1[i], children2[i], blackList=blackList) # Log flags we want to trap on flags = GLib.LogLevelFlags.LEVEL_WARNING | GLib.LogLevelFlags.LEVEL_ERROR \ | GLib.LogLevelFlags.LEVEL_CRITICAL | GLib.LogLevelFlags.FLAG_FATAL | GLib.LogLevelFlags.FLAG_RECURSION GLib.log_set_handler (None, flags, LogTrap, None) # Create test data directory if needed try: os.makedirs (TEST_DATA_PATH) except OSError: if not os.path.isdir (TEST_DATA_PATH): raise libinstpatch-1.1.6/tests/sample_list_test.c000066400000000000000000000165271400263525300210770ustar00rootroot00000000000000/* * sample_list_test.c - Tests sample edit lists and virtual sample store * * - Creates double stereo format test waveform with triangle wave in left * channel and sine wave in right channel * - Re-constructs both channels using 2 sample lists using various list * operations * - Creates virtual sample store using double stereo format with left * and right sample lists * - Duplicates virtual sample store to a double stereo format store * - Compares final duplicated store to original waveform and makes sure its * the same * * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #include #include #define DEFAULT_AUDIO_SIZE (32 * 1024) /* default test waveform size in samples */ #define WAVEFORM_PERIOD 1684 /* something to exercise LSB bytes too */ #define MAX_DIFF_ALLOWED 0.0 /* maximum difference allowed */ #define WAVEFORM_QUARTER (WAVEFORM_PERIOD / 4) static int test_size = DEFAULT_AUDIO_SIZE; static gboolean verbose = FALSE; static GOptionEntry entries[] = { { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be verbose", NULL }, { NULL } }; int main(int argc, char *argv[]) { IpatchSampleStore *store, *vstore, *finstore; IpatchSampleList *rlist, *llist; IpatchSampleData *data, *vdata; double *dwave, *findata; guint periodpos; double d, maxdiff; int i, maxindex; GError *error = NULL; GOptionContext *context; int test_size_q; context = g_option_context_new("- test libInstPatch sample edit list functions"); g_option_context_add_main_entries(context, entries, "sample_list_test"); if(!g_option_context_parse(context, &argc, &argv, &error)) { printf("Failed to parse command line arguments: %s\n", ipatch_gerror_message(error)); return 1; } g_option_context_free(context); test_size_q = test_size / 4; /* test size quarter */ ipatch_init(); /* allocate audio buffer (double format stereo) */ dwave = g_new(double, test_size * 2); /* ++ alloc */ /* generate triangle waveform for left channel and sine wave for right */ for(i = 0; i < test_size * 2; i += 2) { periodpos = i % WAVEFORM_PERIOD; if(periodpos <= WAVEFORM_QUARTER) { dwave[i] = periodpos / (double)WAVEFORM_QUARTER; } else if(periodpos <= WAVEFORM_QUARTER * 3) { dwave[i] = -((periodpos - WAVEFORM_QUARTER) / (double)WAVEFORM_QUARTER - 1.0); } else { dwave[i] = (periodpos - WAVEFORM_QUARTER * 3) / (double)WAVEFORM_QUARTER - 1.0; } dwave[i + 1] = sin((periodpos / (double)WAVEFORM_PERIOD) * G_PI * 2.0); } data = ipatch_sample_data_new(test_size); /* ++ ref */ /* ++ ref - create sample store object for original waveform */ store = ipatch_sample_store_new(IPATCH_TYPE_SAMPLE_STORE_RAM, data, "format", IPATCH_SAMPLE_DOUBLE | IPATCH_SAMPLE_STEREO | IPATCH_SAMPLE_ENDIAN_HOST, "location", dwave, "active", TRUE, NULL); rlist = ipatch_sample_list_new(); /* re-construct right channel in a very inefficient way ;) */ ipatch_sample_list_prepend(rlist, store, test_size_q * 2, test_size_q, IPATCH_SAMPLE_LIST_CHAN_RIGHT); ipatch_sample_list_prepend(rlist, store, 0, test_size_q, IPATCH_SAMPLE_LIST_CHAN_RIGHT); ipatch_sample_list_insert_index(rlist, 1, store, test_size_q, test_size_q, IPATCH_SAMPLE_LIST_CHAN_RIGHT); ipatch_sample_list_append(rlist, store, test_size_q * 3, test_size_q, IPATCH_SAMPLE_LIST_CHAN_RIGHT); /* cut a segment out which overlaps segments and then re-insert the cut seg */ ipatch_sample_list_cut(rlist, test_size_q + test_size_q / 2, test_size_q); ipatch_sample_list_insert(rlist, test_size_q + test_size_q / 2, store, test_size_q + test_size_q / 2, test_size_q, IPATCH_SAMPLE_LIST_CHAN_RIGHT); llist = ipatch_sample_list_new(); /* have fun with left channel too */ ipatch_sample_list_append(llist, store, 0, test_size, IPATCH_SAMPLE_LIST_CHAN_LEFT); ipatch_sample_list_cut(llist, test_size_q, test_size_q); ipatch_sample_list_insert(llist, test_size_q, store, test_size_q, test_size_q, IPATCH_SAMPLE_LIST_CHAN_LEFT); vdata = ipatch_sample_data_new(test_size); /* ++ ref */ /* ++ ref - create virtual store from left and right sample lists */ vstore = ipatch_sample_store_new(IPATCH_TYPE_SAMPLE_STORE_VIRTUAL, vdata, "format", IPATCH_SAMPLE_DOUBLE | IPATCH_SAMPLE_STEREO | IPATCH_SAMPLE_ENDIAN_HOST, NULL); ipatch_sample_store_virtual_set_list(vstore, 0, llist); ipatch_sample_store_virtual_set_list(vstore, 1, rlist); ipatch_sample_store_activate(vstore); /* ++ ref - Duplicate store to render final waveform */ finstore = ipatch_sample_store_duplicate(vstore, IPATCH_TYPE_SAMPLE_STORE_RAM, IPATCH_SAMPLE_DOUBLE | IPATCH_SAMPLE_STEREO | IPATCH_SAMPLE_ENDIAN_HOST, &error); if(!finstore) { printf("Failed to create new duplicate sample store: %s", ipatch_gerror_message(error)); return 1; } findata = ipatch_sample_store_RAM_get_location(finstore); /* compare final waveform against original */ for(i = 0, maxdiff = 0.0, maxindex = 0; i < test_size * 2; i++) { d = dwave[i] - findata[i]; if(d < 0.0) { d = -d; } if(d > maxdiff) { maxdiff = d; maxindex = i; } } /* free up objects and data (for memcheck) */ g_object_unref(data); g_object_unref(vdata); g_object_unref(store); g_object_unref(vstore); g_object_unref(finstore); g_free(dwave); if(maxdiff > MAX_DIFF_ALLOWED) { printf("Sample list test failed: maxdiff=%0.16f index=%d\n", maxdiff, maxindex); return 1; } if(verbose) printf("Sample list test passed: maxdiff=%0.16f index=%d\n", maxdiff, maxindex); else { printf("Sample list and virtual sample store test passed\n"); } return 0; } libinstpatch-1.1.6/tests/sample_test.c000066400000000000000000000171471400263525300200430ustar00rootroot00000000000000/* * sample_test.c - Sample audio conversion tests * * Tests every combination of audio format conversions (484 combinations). * This is done by creating a double audio format triangle waveform and then * for each transformation format pair, converting this waveform to the * first format, then the second, then back to double again and comparing * against the original. * * libInstPatch * Copyright (C) 1999-2014 Element Green * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; version 2.1 * of the License only. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA or on the web at http://www.gnu.org. */ #include #define DEFAULT_AUDIO_SIZE (32 * 1024) /* default test waveform size in samples */ #define WAVEFORM_PERIOD 1684 /* something to exercise LSB bytes too */ #define MAX_DIFF_ALLOWED 0.016 /* maximum difference allowed */ #define WAVEFORM_QUARTER (WAVEFORM_PERIOD / 4) /* all available sample format combinations */ int testformats[] = { IPATCH_SAMPLE_8BIT, IPATCH_SAMPLE_16BIT, IPATCH_SAMPLE_24BIT, IPATCH_SAMPLE_32BIT, IPATCH_SAMPLE_FLOAT, IPATCH_SAMPLE_DOUBLE, IPATCH_SAMPLE_REAL24BIT, IPATCH_SAMPLE_8BIT | IPATCH_SAMPLE_UNSIGNED, IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_UNSIGNED, IPATCH_SAMPLE_24BIT | IPATCH_SAMPLE_UNSIGNED, IPATCH_SAMPLE_32BIT | IPATCH_SAMPLE_UNSIGNED, IPATCH_SAMPLE_REAL24BIT | IPATCH_SAMPLE_UNSIGNED, IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_BENDIAN, IPATCH_SAMPLE_24BIT | IPATCH_SAMPLE_BENDIAN, IPATCH_SAMPLE_32BIT | IPATCH_SAMPLE_BENDIAN, IPATCH_SAMPLE_FLOAT | IPATCH_SAMPLE_BENDIAN, IPATCH_SAMPLE_DOUBLE | IPATCH_SAMPLE_BENDIAN, IPATCH_SAMPLE_REAL24BIT | IPATCH_SAMPLE_BENDIAN, IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_UNSIGNED | IPATCH_SAMPLE_BENDIAN, IPATCH_SAMPLE_24BIT | IPATCH_SAMPLE_UNSIGNED | IPATCH_SAMPLE_BENDIAN, IPATCH_SAMPLE_32BIT | IPATCH_SAMPLE_UNSIGNED | IPATCH_SAMPLE_BENDIAN, IPATCH_SAMPLE_REAL24BIT | IPATCH_SAMPLE_UNSIGNED | IPATCH_SAMPLE_BENDIAN }; #define SAMPLE_FORMAT_COUNT G_N_ELEMENTS (testformats) static int test_size = DEFAULT_AUDIO_SIZE; static gboolean verbose = FALSE; static GOptionEntry entries[] = { { "size", 's', 0, G_OPTION_ARG_INT, &test_size, "Size of test waveform in samples (default=32768)", NULL }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be verbose", NULL }, { NULL } }; int main(int argc, char *argv[]) { IpatchSampleTransform *trans; gpointer srctrans, desttrans; double *dwave, *fintrans; guint periodpos; int srcform, destform; int isrc, idest; double d, maxdiff; int i, maxindex, failcount = 0; GError *error = NULL; GOptionContext *context; context = g_option_context_new("- test libInstPatch sample format conversions"); g_option_context_add_main_entries(context, entries, "sample_test"); if(!g_option_context_parse(context, &argc, &argv, &error)) { printf("Failed to parse command line arguments: %s\n", ipatch_gerror_message(error)); return 1; } g_option_context_free(context); ipatch_init(); dwave = g_new(double, test_size); /* allocate audio buffer (double format) */ srctrans = g_malloc(8 * test_size); /* source format transform buf */ desttrans = g_malloc(8 * test_size); /* destination format transform buf */ fintrans = g_malloc(8 * test_size); /* final transform buf */ /* create sample transform object and allocate its buffer */ trans = ipatch_sample_transform_new(0, 0, 0); ipatch_sample_transform_alloc_size(trans, 32 * 1024); /* generate triangle waveform */ for(i = 0; i < test_size; i++) { periodpos = i % WAVEFORM_PERIOD; if(periodpos <= WAVEFORM_QUARTER) { dwave[i] = periodpos / (double)WAVEFORM_QUARTER; } else if(periodpos <= WAVEFORM_QUARTER * 3) { dwave[i] = -((periodpos - WAVEFORM_QUARTER) / (double)WAVEFORM_QUARTER - 1.0); } else { dwave[i] = (periodpos - WAVEFORM_QUARTER * 3) / (double)WAVEFORM_QUARTER - 1.0; } } /* Convert between all possible format pairs using the following steps: * - Convert double to first format of format pair * - Convert from first format to second format * - Convert second format back to double * - Calculate maximum sample difference between new double audio and original */ for(isrc = 0; isrc < SAMPLE_FORMAT_COUNT; isrc++) { srcform = testformats[isrc]; for(idest = 0; idest < SAMPLE_FORMAT_COUNT; idest++) { destform = testformats[idest]; /* convert double waveform to source format */ ipatch_sample_transform_set_formats(trans, IPATCH_SAMPLE_DOUBLE | IPATCH_SAMPLE_ENDIAN_HOST, srcform, IPATCH_SAMPLE_UNITY_CHANNEL_MAP); ipatch_sample_transform_convert(trans, dwave, srctrans, test_size); /* convert source format to destination format */ ipatch_sample_transform_set_formats(trans, srcform, destform, IPATCH_SAMPLE_UNITY_CHANNEL_MAP); ipatch_sample_transform_convert(trans, srctrans, desttrans, test_size); /* convert destination format to final double output */ ipatch_sample_transform_set_formats(trans, destform, IPATCH_SAMPLE_DOUBLE | IPATCH_SAMPLE_ENDIAN_HOST, IPATCH_SAMPLE_UNITY_CHANNEL_MAP); ipatch_sample_transform_convert(trans, desttrans, fintrans, test_size); /* compare final waveform against original */ for(i = 0, maxdiff = 0.0, maxindex = 0; i < test_size; i++) { d = dwave[i] - fintrans[i]; if(d < 0.0) { d = -d; } if(d > maxdiff) { maxdiff = d; maxindex = i; } } if(verbose) printf("Convert format %03x to %03x%s: maxdiff=%0.6f, sample=%d\n", srcform, destform, maxdiff > MAX_DIFF_ALLOWED ? " FAILED" : "", maxdiff, maxindex); if(maxdiff > MAX_DIFF_ALLOWED) { if(!verbose) printf("Convert format %03x to %03x FAILED: maxdiff=%0.6f, sample=%d\n", srcform, destform, maxdiff, maxindex); failcount++; } } } ipatch_sample_transform_free(trans); g_free(dwave); g_free(srctrans); g_free(desttrans); g_free(fintrans); if(failcount > 0) { printf("%d of %d format conversions FAILED\n", failcount, SAMPLE_FORMAT_COUNT * SAMPLE_FORMAT_COUNT); return 1; } printf("All %d sample format conversions PASSED\n", SAMPLE_FORMAT_COUNT * SAMPLE_FORMAT_COUNT); return 0; } libinstpatch-1.1.6/utils/000077500000000000000000000000001400263525300153435ustar00rootroot00000000000000libinstpatch-1.1.6/utils/CMakeLists.txt000066400000000000000000000013011400263525300200760ustar00rootroot00000000000000include_directories ( ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${GLIB_INCLUDEDIR} ${GLIB_INCLUDE_DIRS} ${GOBJECT_INCLUDEDIR} ${GOBJECT_INCLUDE_DIRS} ${SNDFILE_INCLUDEDIR} ${SNDFILE_INCLUDE_DIRS} ) if( WIN32 ) # disable deprecation warnings add_definitions ( -D_CRT_SECURE_NO_WARNINGS ) endif( WIN32 ) link_directories ( ${GLIB_LIBDIR} ${GLIB_LIBRARY_DIRS} ${GOBJECT_LIBDIR} ${GOBJECT_LIBRARY_DIRS} ${SNDFILE_LIBDIR} ${SNDFILE_LIBRARY_DIRS} ) add_executable ( riff_dump riff_dump.c ) add_dependencies( riff_dump libinstpatch ) target_link_libraries( riff_dump libinstpatch ) libinstpatch-1.1.6/utils/ipatch_convert.c000066400000000000000000000066551400263525300205330ustar00rootroot00000000000000/* in progress, i.e. not yet working */ /* * instpatch.c - A command line interface to libInstPatch * * libInstPatch * Copyright (C) 1999-2014 Element Green * * 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; version 2 * of the License only. * * 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 or point your web browser to http://www.gnu.org. */ #include #include #define _GNU_SOURCE #include static void usage(void); extern char *optarg; extern int optind, opterr, optopt; int main(int argc, char *argv[]) { int option_index = 0; int c; static struct option long_options[] = { {"convert", 1, 0, 'c'}, {"output", 1, 0, 'o'}, {NULL, 0, NULL, 0} }; while(TRUE) { c = getopt_long(argc, argv, "c:o:", long_options, &option_index); if(c == -1) { break; } switch(c) { case 'c': /* conversion type */ break; case 'o': /* conversion output file name/dir */ break; case ':': /* missing option */ fprintf(stderr, "Missing parameter for option '-%c, %s'\n", (char)(long_options[option_index].val), long_options[option_index].name); usage(); exit(1); break; case '?': /* unknown switch */ usage(); exit(1); break; default: fprintf(stderr, "Unknown getopt return val '%d'\n", c); exit(1); break; } } } #define U(text) fprintf (stderr, text); static void usage(void) { U("libInstPatch utility for MIDI instrument patch file processing\n"); U("Copyright (C) 1999-2005 Josh Green \n"); U("Distributed under the LGPL license\n\n"); U("Usage: instpatch [OPTION]... [FILE]...\n\n"); U("Examples:\n"); U(" instpatch -n sf2 -o newfile.sf2 # Create a new SoundFont file.\n"); U(" instpatch -c dls2 *.sf2 # Convert all SoundFont files to DLS2.\n"); U(" instpatch -ql myfile.gig # List items in a GigaSampler file.\n"); U(" instpatch -qp -i 32 example.dls # Get properties of item 32 in DLS file.\n"); U("\n"); U("Query options (with -q or --query):\n"); U(" -i, --item=ID Select item by numeric ID\n"); U(" -l, --list List items\n"); U(" -p, --prop Query properties\n"); U("\n"); U("Conversion options (with -c or --convert):\n"); U(" -t, --type=TYPE File TYPE to convert to\n"); U(" -n, --new=TYPE Create a new patch object of TYPE\n"); U(" -c, --convert=TYPE Convert type of file(s)\n"); U(" -o, --output=file/dir Specify output file name and/or directory for --new and --convert\n"); } libinstpatch-1.1.6/utils/riff_dump.c000066400000000000000000000224761400263525300174750ustar00rootroot00000000000000/* * riff_dump.c - Command line utility to dump info about RIFF files * * riff_dump utility (licensed separately from libInstPatch) * Copyright (C) 1999-2014 Element Green * Public Domain, use as you please. * * Usage: * riff_dump [OPTION] file.sf2 * * Help options: * -h, --help Display help options * * Application options : * -d, --dump=CHUNK_INDEX Dump a chunk by CHUNK_INDEX index * -t, --dump-type=CHNK Dump a chunk by RIFF FOURCC CHNK * -r, --raw Do raw dump rather than formatted hex dump * */ #include #include static gboolean recurse_riff_chunks(IpatchRiff *riff, char *indent, GError **err); static void display_chunk(IpatchRiff *riff, char *indent); static gboolean dump_chunk(IpatchRiff *riff, GError **err); /* options arguments (global for convenience)*/ static gint dump_index = -1; /* set to chunk index if chunk dump requested */ static gchar *dump_type = NULL; /* set to 4 char string if dumping a chunk type */ static gboolean raw_dump = FALSE; /* set to TRUE for raw byte dumps */ /* dislaying variables (global for convenience)*/ static int chunk_index = 0; /* current index */ static gboolean display = TRUE; /* set to FALSE to not display chunks */ static gboolean stop = FALSE; /* set to TRUE to stop recursion */ int main(int argc, char *argv[]) { /* riff file variables */ char *file_name = NULL; /* file name */ IpatchFile *riff_file; /* riff file to open */ IpatchFileHandle *fhandle = NULL; /* riff file file handle */ IpatchRiff *riff; IpatchRiffChunk *chunk; char indent_buf[256] = ""; /* indentation buffer */ GError *err = NULL; static gchar **file_arg = NULL; /* file name argument */ /* parsing context variables */ GOptionContext *context = NULL; /* parsing context */ /* option entries to parse */ static GOptionEntry entries[] = { { "dump", 'd', 0, G_OPTION_ARG_INT, &dump_index, "Dump a chunk by CHUNK_INDEX index", "CHUNK_INDEX" }, { "dump-type", 't', 0, G_OPTION_ARG_STRING, &dump_type, "Dump a chunk by RIFF FOURCC CHNK", "CHNK" }, { "raw", 'r', 0, G_OPTION_ARG_NONE, &raw_dump, "Do raw dump rather than formatted hex dump", NULL }, { G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &file_arg, NULL, "file.sf2" }, { NULL } }; /* parse option in command line */ context = g_option_context_new(NULL); g_option_context_add_main_entries(context, entries, NULL); if(!g_option_context_parse(context, &argc, &argv, &err)) { g_print("option parsing failed: %s\n", err->message); g_error_free(err); g_strfreev(file_arg); g_free(dump_type); return (1); } /* prepare variable for displaying */ if(dump_index >= 0) { display = FALSE; /* we enable display when we find chunk */ } else if(dump_type) { display = FALSE; } /* take the file name argument */ if(file_arg) { file_name = *file_arg; } /* libinstpatch initialization */ ipatch_init(); riff_file = ipatch_file_new(); if(file_name) { fhandle = ipatch_file_open(riff_file, file_name, "r", &err); } if(!(fhandle)) { fprintf(stderr, "Failed to open file '%s': %s\n", file_name, err ? err->message : ""); g_free(dump_type); g_strfreev(file_arg); return (1); } riff = ipatch_riff_new(fhandle); /* start reading chunk */ if(!(chunk = ipatch_riff_start_read(riff, &err))) { fprintf(stderr, "Failed to start RIFF parse of file '%s': %s\n", file_name, err ? err->message : ""); g_free(dump_type); g_strfreev(file_arg); return (1); } /* if a dump of chunk 0 requested or type matches, display everything */ if(dump_index == 0 || (dump_type && strncmp(dump_type, chunk->idstr, 4) == 0)) { display = TRUE; } if(display) { display_chunk(riff, indent_buf); } chunk_index++; strcat(indent_buf, " "); /* continue to read by recursion */ if(!recurse_riff_chunks(riff, indent_buf, &err)) { fprintf(stderr, "%s\n", ipatch_riff_message_detail (riff, -1, "Error while parsing RIFF file '%s': %s", file_name, err ? err->message : "")); g_free(dump_type); g_strfreev(file_arg); return (1); } g_free(dump_type); /* free dump type */ dump_type = NULL; /* not needed but a good practice */ g_strfreev(file_arg); /* free file arguments array */ file_arg = NULL; /* not needed but a good practice */ return (0); } gboolean recurse_riff_chunks(IpatchRiff *riff, char *indent, GError **err) { IpatchRiffChunk *chunk; gboolean retval; while(!stop && (chunk = ipatch_riff_read_chunk(riff, err))) { if(dump_index == chunk_index) /* dump by chunk index match? */ { if(chunk->type != IPATCH_RIFF_CHUNK_SUB) /* list chunk? */ { display_chunk(riff, indent); strcat(indent, " "); display = TRUE; retval = recurse_riff_chunks(riff, indent, err); stop = TRUE; return (retval); } else { retval = dump_chunk(riff, err); /* hex dump of sub chunk */ stop = TRUE; return (retval); } } /* dump by type match? */ else if(dump_type && strncmp(dump_type, chunk->idstr, 4) == 0) { if(chunk->type != IPATCH_RIFF_CHUNK_SUB) /* list chunk? */ { display = TRUE; strcat(indent, " "); recurse_riff_chunks(riff, indent, err); indent[strlen(indent) - 2] = '\0'; display = FALSE; } else { dump_chunk(riff, err); /* hex dump of sub chunk */ } } else /* no dump match, just do stuff */ { if(display) { display_chunk(riff, indent); } chunk_index++; /* advance chunk index */ if(chunk->type != IPATCH_RIFF_CHUNK_SUB) /* list chunk? */ { strcat(indent, " "); if(!recurse_riff_chunks(riff, indent, err)) { return (FALSE); } indent[strlen(indent) - 2] = '\0'; } } if(!ipatch_riff_close_chunk(riff, -1, err)) { return (FALSE); } } return (ipatch_riff_get_error(riff, NULL)); } void display_chunk(IpatchRiff *riff, char *indent) { IpatchRiffChunk *chunk; int filepos; chunk = ipatch_riff_get_chunk(riff, -1); filepos = ipatch_riff_get_position(riff); if(chunk->type == IPATCH_RIFF_CHUNK_SUB) printf("%s(%.4s)[%4d] (ofs = 0x%x, size = %d)\n", indent, chunk->idstr, chunk_index, filepos - (chunk->position + IPATCH_RIFF_HEADER_SIZE), chunk->size); else /* list chunk */ printf("%s<%.4s>[%4d] (ofs = 0x%x, size = %d)\n", indent, chunk->idstr, chunk_index, filepos - (chunk->position + IPATCH_RIFF_HEADER_SIZE), chunk->size); } #define BUFFER_SIZE (16 * 1024) /* hex dump of a sub chunk */ gboolean dump_chunk(IpatchRiff *riff, GError **err) { IpatchRiffChunk *chunk; guint8 buf[BUFFER_SIZE]; int filepos, read_size, bytes_left, i; chunk = ipatch_riff_get_chunk(riff, -1); filepos = ipatch_riff_get_position(riff); if(!raw_dump) { printf("Dump chunk: (%.4s)[%4d] (ofs = 0x%x, size = %d)", chunk->idstr, chunk_index, filepos - (chunk->position + IPATCH_RIFF_HEADER_SIZE), chunk->size); i = filepos & ~0xF; /* round down to nearest 16 byte offset */ while(i < filepos) /* advance to start point in 16 byte block */ { if(!(i & 0xF)) { printf("\n%08u ", i); /* print file position */ } else if(!(i & 0x3)) { printf(" | "); /* print divider */ } printf(" "); /* skip 1 byte character */ i++; } } read_size = BUFFER_SIZE; bytes_left = chunk->size; while(bytes_left) /* loop until chunk exhausted */ { if(bytes_left < BUFFER_SIZE) { read_size = bytes_left; } if(!ipatch_file_read(riff->handle, &buf, read_size, err)) { return (FALSE); } for(i = 0; i < read_size; i++, filepos++) { if(!raw_dump) { if(!(filepos & 0xF)) { printf("\n%08u ", filepos); /* print file position */ } else if(!(filepos & 0x3)) { printf(" | "); /* print divider */ } } printf("%02X ", buf[i]); } bytes_left -= read_size; } printf("\n"); return (TRUE); }