View Issue Details

IDProjectCategoryView StatusLast Update
0001241OpenMPT[All Projects] libopenmptpublic2019-10-14 18:27
Reportermanx Assigned Tomanx  
PrioritynormalSeverityminorReproducibilityalways
Status assignedResolutionopen 
Product VersionOpenMPT 1.29.00.* (current testing) 
Target VersionOpenMPT 1.29 / libopenmpt 0.5 (goals)Fixed in Version 
Summary0001241: std::string_view for ctl key, and non-string overloads for ctl_get and ctl_set
Description

Providing overloads for std::int64_t and double in addition to std::string avoids to/from string conversions for ctls that actually want a number type.

We should also consider using std::string_view for the ctl key.

TagsNo tags attached.
Has the bug occurred in previous versions?
Tested code revision (in case you know it)

Relationships

related to 0001240 assignedmanx Modernize C++ API 

Activities

manx

manx

2019-10-03 08:41

administrator   ~0004102

Proving overloads for existing string API functions for string_view can break existing code if it relies on implicit conversion because that would now be ambiguous. We cannot solve all possible cases without relying on template functions with std::enable_if here (which would be ugly), however we could additionally add overloads for the common case of string literals (and cstr). In any case, this would be a potentially breaking API change unless we use a new function name.

manx

manx

2019-10-14 18:25

administrator  

libopenmpt-api-cpp17-v3.patch (51,847 bytes)
Index: common/mptString.cpp
===================================================================
--- common/mptString.cpp	(revision 12197)
+++ common/mptString.cpp	(working copy)
@@ -1793,6 +1793,28 @@
 	return a.length() < b.length() ? -1 : 1;
 }
 
+int CompareNoCaseAscii(std::string_view a, std::string_view b)
+{
+	for(std::size_t i = 0; i < std::min(a.length(), b.length()); ++i)
+	{
+		unsigned char ac = static_cast<unsigned char>(mpt::ToLowerCaseAscii(a[i]));
+		unsigned char bc = static_cast<unsigned char>(mpt::ToLowerCaseAscii(b[i]));
+		if(ac != bc)
+		{
+			return ac < bc ? -1 : 1;
+		} else if(!ac && !bc)
+		{
+			return 0;
+		}
+	}
+	if(a.length() == b.length())
+	{
+		return 0;
+	}
+	return a.length() < b.length() ? -1 : 1;
+}
+
+
 #if defined(MODPLUG_TRACKER)
 
 mpt::ustring ToLowerCase(const mpt::ustring &s)
Index: common/mptString.h
===================================================================
--- common/mptString.h	(revision 12197)
+++ common/mptString.h	(working copy)
@@ -19,6 +19,7 @@
 #include <algorithm>
 #include <limits>
 #include <string>
+#include <string_view>
 
 #include <cstring>
 
@@ -525,6 +526,7 @@
 
 int CompareNoCaseAscii(const char *a, const char *b, std::size_t n);
 int CompareNoCaseAscii(const std::string &a, const std::string &b);
+int CompareNoCaseAscii(std::string_view a, std::string_view b);
 
 #if defined(MODPLUG_TRACKER)
 
Index: common/stdafx.h
===================================================================
--- common/stdafx.h	(revision 12197)
+++ common/stdafx.h	(working copy)
@@ -115,6 +115,7 @@
 // <algorithm>
 // <limits>
 // <string>
+// <string_view>
 // <type_traits>
 // <cstring>
 
Index: libopenmpt/dox/changelog.md
===================================================================
--- libopenmpt/dox/changelog.md	(revision 12197)
+++ libopenmpt/dox/changelog.md	(working copy)
@@ -15,9 +15,9 @@
  *  [**Regression**] foo_openmpt: foo_openmpt is discontinued. Please use
     Kode54's fork foo_openmpt54:
     <https://www.foobar2000.org/components/view/foo_openmpt54>.
- *  [**Regression**] Support for client code using C++11 has been removed. C++14
-    is now required to build libopenmpt client applications.
- *  [**Regression**] Support for building with C++11 and C++14 has been removed.
+ *  [**Regression**] Support for client code using C++11 or C++ 14 has been
+    removed. C++17 is now required to build libopenmpt client applications.
+ *  [**Regression**] Support for building with C++11 or C++14 has been removed.
     C++17 is now required to build libopenmpt.
  *  [**Regression**] Support for Visual Studio 2015 has been removed.
  *  [**Regression**] Support for GCC 4.8, 4.9, 5, 6 has been removed.
Index: libopenmpt/dox/dependencies.md
===================================================================
--- libopenmpt/dox/dependencies.md	(revision 12197)
+++ libopenmpt/dox/dependencies.md	(working copy)
@@ -48,8 +48,7 @@
  *  Required compilers to use libopenmpt:
      *  Any **C89** / **C99** / **C11** compatible compiler should work with
         the C API as long as a **C99** compatible **stdint.h** is available.
-     *  Any **C++14** / **C++17** compatible compiler should work with the C++
-        API.
+     *  Any **C++17** compatible compiler should work with the C++ API.
  *  **J2B** support requires an inflate (deflate decompression) implementation:
      *  **zlib**
      *  **miniz** can be used internally if no zlib is available.
Index: libopenmpt/libopenmpt.h
===================================================================
--- libopenmpt/libopenmpt.h	(revision 12197)
+++ libopenmpt/libopenmpt.h	(working copy)
@@ -1396,21 +1396,21 @@
  * \param mod The module handle to work on.
  * \return A semicolon-separated list containing all supported ctl keys.
  * \remarks Currently supported ctl values are:
- *          - load.skip_samples: Set to "1" to avoid loading samples into memory
- *          - load.skip_patterns: Set to "1" to avoid loading patterns into memory
- *          - load.skip_plugins: Set to "1" to avoid loading plugins
- *          - load.skip_subsongs_init: Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking.
- *          - seek.sync_samples: Set to "1" to sync sample playback when using openmpt_module_set_position_seconds or openmpt_module_set_position_order_row.
- *          - subsong: The current subsong. Setting it has identical semantics as openmpt_module_select_subsong(), getting it returns the currently selected subsong.
- *          - play.at_end: Chooses the behaviour when the end of song is reached:
+ *          - load.skip_samples (boolean): Set to "1" to avoid loading samples into memory
+ *          - load.skip_patterns (boolean): Set to "1" to avoid loading patterns into memory
+ *          - load.skip_plugins (boolean): Set to "1" to avoid loading plugins
+ *          - load.skip_subsongs_init (boolean): Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking.
+ *          - seek.sync_samples (boolean): Set to "1" to sync sample playback when using openmpt_module_set_position_seconds or openmpt_module_set_position_order_row.
+ *          - subsong (integer): The current subsong. Setting it has identical semantics as openmpt_module_select_subsong(), getting it returns the currently selected subsong.
+ *          - play.at_end (text): Chooses the behaviour when the end of song is reached:
  *                         - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames.
  *                         - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the song start or loop start.
  *                         - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames.
- *          - play.tempo_factor: Set a floating point tempo factor. "1.0" is the default tempo.
- *          - play.pitch_factor: Set a floating point pitch factor. "1.0" is the default pitch.
- *          - render.resampler.emulate_amiga: Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting.
- *          - render.opl.volume_factor: Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume.
- *          - dither: Set the dither algorithm that is used for the 16 bit versions of openmpt_module_read. Supported values are:
+ *          - play.tempo_factor (floatingpoint): Set a floating point tempo factor. "1.0" is the default tempo.
+ *          - play.pitch_factor (floatingpoint): Set a floating point pitch factor. "1.0" is the default pitch.
+ *          - render.resampler.emulate_amiga (boolean): Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting.
+ *          - render.opl.volume_factor (floatingpoint): Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume.
+ *          - dither (integer): Set the dither algorithm that is used for the 16 bit versions of openmpt_module_read. Supported values are:
  *                    - 0: No dithering.
  *                    - 1: Default mode. Chosen by OpenMPT code, might change.
  *                    - 2: Rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker).
@@ -1417,6 +1417,7 @@
  *                    - 3: Rectangular, 1 bit depth, simple 1st order noise shaping
  */
 LIBOPENMPT_API const char * openmpt_module_get_ctls( openmpt_module * mod );
+
 /*! \brief Get current ctl value
  *
  * \param mod The module handle to work on.
@@ -1423,8 +1424,46 @@
  * \param ctl The ctl key whose value should be retrieved.
  * \return The associated ctl value, or NULL on failure.
  * \sa openmpt_module_get_ctls
+ * \deprecated Please use openmpt_module_ctl_get_boolean(), openmpt_module_ctl_get_integer(), openmpt_module_ctl_get_floatingpoint(), or openmpt_module_ctl_get_text().
  */
-LIBOPENMPT_API const char * openmpt_module_ctl_get( openmpt_module * mod, const char * ctl );
+LIBOPENMPT_API LIBOPENMPT_DEPRECATED const char * openmpt_module_ctl_get( openmpt_module * mod, const char * ctl );
+/*! \brief Get current ctl boolean value
+ *
+ * \param mod The module handle to work on.
+ * \param ctl The ctl key whose value should be retrieved.
+ * \return The associated ctl value, or NULL on failure.
+ * \sa openmpt_module_get_ctls
+ * \since 0.5.0
+ */
+LIBOPENMPT_API int openmpt_module_ctl_get_boolean( openmpt_module * mod, const char * ctl );
+/*! \brief Get current ctl integer value
+ *
+ * \param mod The module handle to work on.
+ * \param ctl The ctl key whose value should be retrieved.
+ * \return The associated ctl value, or NULL on failure.
+ * \sa openmpt_module_get_ctls
+ * \since 0.5.0
+ */
+LIBOPENMPT_API int64_t openmpt_module_ctl_get_integer( openmpt_module * mod, const char * ctl );
+/*! \brief Get current ctl floatingpoint value
+ *
+ * \param mod The module handle to work on.
+ * \param ctl The ctl key whose value should be retrieved.
+ * \return The associated ctl value, or NULL on failure.
+ * \sa openmpt_module_get_ctls
+ * \since 0.5.0
+ */
+LIBOPENMPT_API double openmpt_module_ctl_get_floatingpoint( openmpt_module * mod, const char * ctl );
+/*! \brief Get current ctl string value
+ *
+ * \param mod The module handle to work on.
+ * \param ctl The ctl key whose value should be retrieved.
+ * \return The associated ctl value, or NULL on failure.
+ * \sa openmpt_module_get_ctls
+ * \since 0.5.0
+ */
+LIBOPENMPT_API const char * openmpt_module_ctl_get_text( openmpt_module * mod, const char * ctl );
+
 /*! \brief Set ctl value
  *
  * \param mod The module handle to work on.
@@ -1432,8 +1471,49 @@
  * \param value The value that should be set.
  * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized.
  * \sa openmpt_module_get_ctls
+ * \deprecated Please use openmpt_module_ctl_set_boolean(), openmpt_module_ctl_set_integer(), openmpt_module_ctl_set_floatingpoint(), or openmpt_module_ctl_set_text().
  */
-LIBOPENMPT_API int openmpt_module_ctl_set( openmpt_module * mod, const char * ctl, const char * value );
+LIBOPENMPT_API LIBOPENMPT_DEPRECATED int openmpt_module_ctl_set( openmpt_module * mod, const char * ctl, const char * value );
+/*! \brief Set ctl boolean value
+ *
+ * \param mod The module handle to work on.
+ * \param ctl The ctl key whose value should be set.
+ * \param value The value that should be set.
+ * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized.
+ * \sa openmpt_module_get_ctls
+ * \since 0.5.0
+ */
+LIBOPENMPT_API int openmpt_module_ctl_set_boolean( openmpt_module * mod, const char * ctl, int value );
+/*! \brief Set ctl integer value
+ *
+ * \param mod The module handle to work on.
+ * \param ctl The ctl key whose value should be set.
+ * \param value The value that should be set.
+ * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized.
+ * \sa openmpt_module_get_ctls
+ * \since 0.5.0
+ */
+LIBOPENMPT_API int openmpt_module_ctl_set_integer( openmpt_module * mod, const char * ctl, int64_t value );
+/*! \brief Set ctl floatingpoint value
+ *
+ * \param mod The module handle to work on.
+ * \param ctl The ctl key whose value should be set.
+ * \param value The value that should be set.
+ * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized.
+ * \sa openmpt_module_get_ctls
+ * \since 0.5.0
+ */
+LIBOPENMPT_API int openmpt_module_ctl_set_floatingpoint( openmpt_module * mod, const char * ctl, double value );
+/*! \brief Set ctl string value
+ *
+ * \param mod The module handle to work on.
+ * \param ctl The ctl key whose value should be set.
+ * \param value The value that should be set.
+ * \return 1 if successful, 0 in case the value is not sensible (e.g. negative tempo factor) or the ctl is not recognized.
+ * \sa openmpt_module_get_ctls
+ * \since 0.5.0
+ */
+LIBOPENMPT_API int openmpt_module_ctl_set_text( openmpt_module * mod, const char * ctl, const char * value );
 
 /* remember to add new functions to both C and C++ interfaces and to increase OPENMPT_API_VERSION_MINOR */
 
Index: libopenmpt/libopenmpt.hpp
===================================================================
--- libopenmpt/libopenmpt.hpp	(revision 12197)
+++ libopenmpt/libopenmpt.hpp	(working copy)
@@ -17,6 +17,7 @@
 #include <iostream>
 #include <map>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include <cstdint>
@@ -231,8 +232,16 @@
 /*!
   \param extension file extension to query without a leading dot. The case is ignored.
   \return true if the extension is supported by libopenmpt, false otherwise.
+  \deprecated Please use openmpt::is_extension_supported2().
 */
-LIBOPENMPT_CXX_API bool is_extension_supported( const std::string & extension );
+LIBOPENMPT_ATTR_DEPRECATED LIBOPENMPT_CXX_API bool is_extension_supported( const std::string & extension );
+//! Query whether a file extension is supported
+/*!
+  \param extension file extension to query without a leading dot. The case is ignored.
+  \return true if the extension is supported by libopenmpt, false otherwise.
+  \since 0.5.0
+*/
+LIBOPENMPT_CXX_API bool is_extension_supported2( std::string_view extension );
 
 //! Roughly scan the input stream to find out whether libopenmpt might be able to open it
 /*!
@@ -962,21 +971,21 @@
 	/*!
 	  \return A vector containing all supported ctl keys.
 	  \remarks Currently supported ctl values are:
-	           - load.skip_samples: Set to "1" to avoid loading samples into memory
-	           - load.skip_patterns: Set to "1" to avoid loading patterns into memory
-	           - load.skip_plugins: Set to "1" to avoid loading plugins
-	           - load.skip_subsongs_init: Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking.
-	           - seek.sync_samples: Set to "1" to sync sample playback when using openmpt::module::set_position_seconds or openmpt::module::set_position_order_row.
-	           - subsong: The current subsong. Setting it has identical semantics as openmpt::module::select_subsong(), getting it returns the currently selected subsong.
-	           - play.at_end: Chooses the behaviour when the end of song is reached:
+	           - load.skip_samples (boolean): Set to "1" to avoid loading samples into memory
+	           - load.skip_patterns (boolean): Set to "1" to avoid loading patterns into memory
+	           - load.skip_plugins (boolean): Set to "1" to avoid loading plugins
+	           - load.skip_subsongs_init (boolean): Set to "1" to avoid pre-initializing sub-songs. Skipping results in faster module loading but slower seeking.
+	           - seek.sync_samples (boolean): Set to "1" to sync sample playback when using openmpt::module::set_position_seconds or openmpt::module::set_position_order_row.
+	           - subsong (integer): The current subsong. Setting it has identical semantics as openmpt::module::select_subsong(), getting it returns the currently selected subsong.
+	           - play.at_end (text): Chooses the behaviour when the end of song is reached:
 	                          - "fadeout": Fades the module out for a short while. Subsequent reads after the fadeout will return 0 rendered frames.
 	                          - "continue": Returns 0 rendered frames when the song end is reached. Subsequent reads will continue playing from the song start or loop start.
 	                          - "stop": Returns 0 rendered frames when the song end is reached. Subsequent reads will return 0 rendered frames.
-	           - play.tempo_factor: Set a floating point tempo factor. "1.0" is the default tempo.
-	           - play.pitch_factor: Set a floating point pitch factor. "1.0" is the default pitch.
-	           - render.resampler.emulate_amiga: Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. 
-	           - render.opl.volume_factor: Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume.
-	           - dither: Set the dither algorithm that is used for the 16 bit versions of openmpt::module::read. Supported values are:
+	           - play.tempo_factor (floatingpoint): Set a floating point tempo factor. "1.0" is the default tempo.
+	           - play.pitch_factor (floatingpoint): Set a floating point pitch factor. "1.0" is the default pitch.
+	           - render.resampler.emulate_amiga (boolean): Set to "1" to enable the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. 
+	           - render.opl.volume_factor (floatingpoint): Set volume factor applied to synthesized OPL sounds, relative to the default OPL volume.
+	           - dither (integer): Set the dither algorithm that is used for the 16 bit versions of openmpt::module::read. Supported values are:
 	                     - 0: No dithering.
 	                     - 1: Default mode. Chosen by OpenMPT code, might change.
 	                     - 2: Rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker).
@@ -991,8 +1000,42 @@
 	  \param ctl The ctl key whose value should be retrieved.
 	  \return The associated ctl value.
 	  \sa openmpt::module::get_ctls
+	  \deprecated Please use openmpt::module::ctl_get_boolean(), openmpt::module::ctl_get_integer(), openmpt::module::ctl_get_floatingpoint(), or openmpt::module::ctl_get_text().
 	*/
-	std::string ctl_get( const std::string & ctl ) const;
+	LIBOPENMPT_ATTR_DEPRECATED std::string ctl_get( const std::string & ctl ) const;
+	//! Get current ctl boolean value
+	/*!
+	  \param ctl The ctl key whose value should be retrieved.
+	  \return The associated ctl value.
+	  \sa openmpt::module::get_ctls
+	  \since 0.5.0
+	*/
+	bool ctl_get_boolean( std::string_view ctl ) const;
+	//! Get current ctl integer value
+	/*!
+	  \param ctl The ctl key whose value should be retrieved.
+	  \return The associated ctl value.
+	  \sa openmpt::module::get_ctls
+	  \since 0.5.0
+	*/
+	std::int64_t ctl_get_integer( std::string_view ctl ) const;
+	//! Get current ctl floatingpoint value
+	/*!
+	  \param ctl The ctl key whose value should be retrieved.
+	  \return The associated ctl value.
+	  \sa openmpt::module::get_ctls
+	  \since 0.5.0
+	*/
+	double ctl_get_floatingpoint( std::string_view ctl ) const;
+	//! Get current ctl text value
+	/*!
+	  \param ctl The ctl key whose value should be retrieved.
+	  \return The associated ctl value.
+	  \sa openmpt::module::get_ctls
+	  \since 0.5.0
+	*/
+	std::string ctl_get_text( std::string_view ctl ) const;
+
 	//! Set ctl value
 	/*!
 	  \param ctl The ctl key whose value should be set.
@@ -999,8 +1042,45 @@
 	  \param value The value that should be set.
 	  \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls.
 	  \sa openmpt::module::get_ctls
+	  \deprecated Please use openmpt::module::ctl_set_bool(), openmpt::module::ctl_set_int(), openmpt::module::ctl_set_float(), or openmpt::module::ctl_set_string().
 	*/
-	void ctl_set( const std::string & ctl, const std::string & value );
+	LIBOPENMPT_ATTR_DEPRECATED void ctl_set( const std::string & ctl, const std::string & value );
+	//! Set ctl boolean value
+	/*!
+	  \param ctl The ctl key whose value should be set.
+	  \param value The value that should be set.
+	  \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls.
+	  \sa openmpt::module::get_ctls
+	  \since 0.5.0
+	*/
+	void ctl_set_boolean( std::string_view ctl, bool value );
+	//! Set ctl integer value
+	/*!
+	  \param ctl The ctl key whose value should be set.
+	  \param value The value that should be set.
+	  \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls.
+	  \sa openmpt::module::get_ctls
+	  \since 0.5.0
+	*/
+	void ctl_set_integer( std::string_view ctl, std::int64_t value );
+	//! Set ctl floatingpoint value
+	/*!
+	  \param ctl The ctl key whose value should be set.
+	  \param value The value that should be set.
+	  \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls.
+	  \sa openmpt::module::get_ctls
+	  \since 0.5.0
+	*/
+	void ctl_set_floatingpoint( std::string_view ctl, double value );
+	//! Set ctl text value
+	/*!
+	  \param ctl The ctl key whose value should be set.
+	  \param value The value that should be set.
+	  \throws openmpt::exception Throws an exception derived from openmpt::exception in case the value is not sensible (e.g. negative tempo factor) or under the circumstances outlined in openmpt::module::get_ctls.
+	  \sa openmpt::module::get_ctls
+	  \since 0.5.0
+	*/
+	void ctl_set_text( std::string_view ctl, std::string_view value );
 
 	// remember to add new functions to both C and C++ interfaces and to increase OPENMPT_API_VERSION_MINOR
 
Index: libopenmpt/libopenmpt_c.cpp
===================================================================
--- libopenmpt/libopenmpt_c.cpp	(revision 12197)
+++ libopenmpt/libopenmpt_c.cpp	(working copy)
@@ -1278,6 +1278,46 @@
 	}
 	return NULL;
 }
+int openmpt_module_ctl_get_boolean( openmpt_module * mod, const char * ctl ) {
+	try {
+		openmpt::interface::check_soundfile( mod );
+		openmpt::interface::check_pointer( ctl );
+		return mod->impl->ctl_get_boolean( ctl );
+	} catch ( ... ) {
+		openmpt::report_exception( __func__, mod );
+	}
+	return 0;
+}
+int64_t openmpt_module_ctl_get_integer( openmpt_module * mod, const char * ctl ) {
+	try {
+		openmpt::interface::check_soundfile( mod );
+		openmpt::interface::check_pointer( ctl );
+		return mod->impl->ctl_get_integer( ctl );
+	} catch ( ... ) {
+		openmpt::report_exception( __func__, mod );
+	}
+	return 0;
+}
+double openmpt_module_ctl_get_floatingpoint( openmpt_module * mod, const char * ctl ) {
+	try {
+		openmpt::interface::check_soundfile( mod );
+		openmpt::interface::check_pointer( ctl );
+		return mod->impl->ctl_get_floatingpoint( ctl );
+	} catch ( ... ) {
+		openmpt::report_exception( __func__, mod );
+	}
+	return 0.0;
+}
+const char * openmpt_module_ctl_get_text( openmpt_module * mod, const char * ctl ) {
+	try {
+		openmpt::interface::check_soundfile( mod );
+		openmpt::interface::check_pointer( ctl );
+		return openmpt::strdup( mod->impl->ctl_get_text( ctl ).c_str() );
+	} catch ( ... ) {
+		openmpt::report_exception( __func__, mod );
+	}
+	return NULL;
+}
 
 int openmpt_module_ctl_set( openmpt_module * mod, const char * ctl, const char * value ) {
 	try {
@@ -1291,8 +1331,52 @@
 	}
 	return 0;
 }
+int openmpt_module_ctl_set_boolean( openmpt_module * mod, const char * ctl, int value ) {
+	try {
+		openmpt::interface::check_soundfile( mod );
+		openmpt::interface::check_pointer( ctl );
+		mod->impl->ctl_set_boolean( ctl, value );
+		return 1;
+	} catch ( ... ) {
+		openmpt::report_exception( __func__, mod );
+	}
+	return 0;
+}
+int openmpt_module_ctl_set_integer( openmpt_module * mod, const char * ctl, int64_t value ) {
+	try {
+		openmpt::interface::check_soundfile( mod );
+		openmpt::interface::check_pointer( ctl );
+		mod->impl->ctl_set_integer( ctl, value );
+		return 1;
+	} catch ( ... ) {
+		openmpt::report_exception( __func__, mod );
+	}
+	return 0;
+}
+int openmpt_module_ctl_set_floatingpoint( openmpt_module * mod, const char * ctl, double value ) {
+	try {
+		openmpt::interface::check_soundfile( mod );
+		openmpt::interface::check_pointer( ctl );
+		mod->impl->ctl_set_floatingpoint( ctl, value );
+		return 1;
+	} catch ( ... ) {
+		openmpt::report_exception( __func__, mod );
+	}
+	return 0;
+}
+int openmpt_module_ctl_set_text( openmpt_module * mod, const char * ctl, const char * value ) {
+	try {
+		openmpt::interface::check_soundfile( mod );
+		openmpt::interface::check_pointer( ctl );
+		openmpt::interface::check_pointer( value );
+		mod->impl->ctl_set_text( ctl, value );
+		return 1;
+	} catch ( ... ) {
+		openmpt::report_exception( __func__, mod );
+	}
+	return 0;
+}
 
-
 openmpt_module_ext * openmpt_module_ext_create( openmpt_stream_callbacks stream_callbacks, void * stream, openmpt_log_func logfunc, void * loguser, openmpt_error_func errfunc, void * erruser, int * error, const char * * error_message, const openmpt_module_initial_ctl * ctls ) {
 	try {
 		openmpt_module_ext * mod_ext = (openmpt_module_ext*)std::calloc( 1, sizeof( openmpt_module_ext ) );
Index: libopenmpt/libopenmpt_cxx.cpp
===================================================================
--- libopenmpt/libopenmpt_cxx.cpp	(revision 12197)
+++ libopenmpt/libopenmpt_cxx.cpp	(working copy)
@@ -126,6 +126,9 @@
 bool is_extension_supported( const std::string & extension ) {
 	return openmpt::module_impl::is_extension_supported( extension );
 }
+bool is_extension_supported( std::string_view extension ) {
+	return openmpt::module_impl::is_extension_supported( extension );
+}
 
 double could_open_probability( std::istream & stream, double effort, std::ostream & log ) {
 	return openmpt::module_impl::could_open_probability( stream, effort, openmpt::helper::make_unique<std_ostream_log>( log ) );
@@ -385,12 +388,38 @@
 std::vector<std::string> module::get_ctls() const {
 	return impl->get_ctls();
 }
+
 std::string module::ctl_get( const std::string & ctl ) const {
 	return impl->ctl_get( ctl );
 }
+bool module::ctl_get_boolean( std::string_view ctl ) const {
+	return impl->ctl_get_boolean( ctl );
+}
+std::int64_t module::ctl_get_integer( std::string_view ctl ) const {
+	return impl->ctl_get_integer( ctl );
+}
+double module::ctl_get_floatingpoint( std::string_view ctl ) const {
+	return impl->ctl_get_floatingpoint( ctl );
+}
+std::string module::ctl_get_text( std::string_view ctl ) const {
+	return impl->ctl_get_text( ctl );
+}
+
 void module::ctl_set( const std::string & ctl, const std::string & value ) {
 	impl->ctl_set( ctl, value );
 }
+void module::ctl_set_boolean( std::string_view ctl, bool value ) {
+	impl->ctl_set_boolean( ctl, value );
+}
+void module::ctl_set_integer( std::string_view ctl, std::int64_t value ) {
+	impl->ctl_set_integer( ctl, value );
+}
+void module::ctl_set_floatingpoint( std::string_view ctl, double value ) {
+	impl->ctl_set_floatingpoint( ctl, value );
+}
+void module::ctl_set_text( std::string_view ctl, std::string_view value ) {
+	impl->ctl_set_text( ctl, value );
+}
 
 module_ext::module_ext( std::istream & stream, std::ostream & log, const std::map< std::string, std::string > & ctls ) : ext_impl(0) {
 	ext_impl = new module_ext_impl( stream, openmpt::helper::make_unique<std_ostream_log>( log ), ctls );
Index: libopenmpt/libopenmpt_impl.cpp
===================================================================
--- libopenmpt/libopenmpt_impl.cpp	(revision 12197)
+++ libopenmpt/libopenmpt_impl.cpp	(working copy)
@@ -593,12 +593,9 @@
 	std::copy( extensions.begin(), extensions.end(), std::back_insert_iterator<std::vector<std::string> >( retval ) );
 	return retval;
 }
-bool module_impl::is_extension_supported( const char * extension ) {
+bool module_impl::is_extension_supported( std::string_view extension ) {
 	return CSoundFile::IsExtensionSupported( extension );
 }
-bool module_impl::is_extension_supported( const std::string & extension ) {
-	return CSoundFile::IsExtensionSupported( extension.c_str() );
-}
 double module_impl::could_open_probability( const OpenMPT::FileReader & file, double effort, std::unique_ptr<log_interface> log ) {
 	try {
 		if ( effort >= 0.8 ) {
@@ -1527,6 +1524,7 @@
 		"dither",
 	};
 }
+
 std::string module_impl::ctl_get( std::string ctl, bool throw_if_unknown ) const {
 	if ( !ctl.empty() ) {
 		// cppcheck false-positive
@@ -1591,6 +1589,215 @@
 		}
 	}
 }
+bool module_impl::ctl_get_boolean( std::string_view ctl, bool throw_if_unknown ) const {
+	if ( !ctl.empty() ) {
+		// cppcheck false-positive
+		// cppcheck-suppress containerOutOfBounds
+		char rightmost = ctl.back();
+		if ( rightmost == '!' || rightmost == '?' ) {
+			if ( rightmost == '!' ) {
+				throw_if_unknown = true;
+			} else if ( rightmost == '?' ) {
+				throw_if_unknown = false;
+			}
+			ctl = ctl.substr( 0, ctl.length() - 1 );
+		}
+	}
+	if ( ctl == "" ) {
+		throw openmpt::exception("empty ctl");
+	} else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) {
+		return m_ctl_load_skip_samples;
+	} else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) {
+		return m_ctl_load_skip_patterns;
+	} else if ( ctl == "load.skip_plugins" ) {
+		return m_ctl_load_skip_plugins;
+	} else if ( ctl == "load.skip_subsongs_init" ) {
+		return m_ctl_load_skip_subsongs_init;
+	} else if ( ctl == "seek.sync_samples" ) {
+		return m_ctl_seek_sync_samples;
+	} else if ( ctl == "subsong" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.at_end" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.tempo_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.pitch_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "render.resampler.emulate_amiga" ) {
+		return m_sndFile->m_Resampler.m_Settings.emulateAmiga;
+	} else if ( ctl == "render.opl.volume_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "dither" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else {
+		if ( throw_if_unknown ) {
+			throw openmpt::exception("unknown ctl: " + std::string(ctl));
+		} else {
+			return false;
+		}
+	}
+}
+std::int64_t module_impl::ctl_get_integer( std::string_view ctl, bool throw_if_unknown ) const {
+	if ( !ctl.empty() ) {
+		// cppcheck false-positive
+		// cppcheck-suppress containerOutOfBounds
+		char rightmost = ctl.back();
+		if ( rightmost == '!' || rightmost == '?' ) {
+			if ( rightmost == '!' ) {
+				throw_if_unknown = true;
+			} else if ( rightmost == '?' ) {
+				throw_if_unknown = false;
+			}
+			ctl = ctl.substr( 0, ctl.length() - 1 );
+		}
+	}
+	if ( ctl == "" ) {
+		throw openmpt::exception("empty ctl");
+	} else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_plugins" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_subsongs_init" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "seek.sync_samples" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "subsong" ) {
+		return get_selected_subsong();
+	} else if ( ctl == "play.at_end" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.tempo_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.pitch_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "render.resampler.emulate_amiga" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "render.opl.volume_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "dither" ) {
+		return static_cast<int>( m_Dither->GetMode() );
+	} else {
+		if ( throw_if_unknown ) {
+			throw openmpt::exception("unknown ctl: " + std::string(ctl));
+		} else {
+			return 0;
+		}
+	}
+}
+double module_impl::ctl_get_floatingpoint( std::string_view ctl, bool throw_if_unknown ) const {
+	if ( !ctl.empty() ) {
+		// cppcheck false-positive
+		// cppcheck-suppress containerOutOfBounds
+		char rightmost = ctl.back();
+		if ( rightmost == '!' || rightmost == '?' ) {
+			if ( rightmost == '!' ) {
+				throw_if_unknown = true;
+			} else if ( rightmost == '?' ) {
+				throw_if_unknown = false;
+			}
+			ctl = ctl.substr( 0, ctl.length() - 1 );
+		}
+	}
+	if ( ctl == "" ) {
+		throw openmpt::exception("empty ctl");
+	} else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_plugins" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_subsongs_init" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "seek.sync_samples" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "subsong" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.at_end" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.tempo_factor" ) {
+		if ( !is_loaded() ) {
+			return 1.0;
+		}
+		return 65536.0 / m_sndFile->m_nTempoFactor;
+	} else if ( ctl == "play.pitch_factor" ) {
+		if ( !is_loaded() ) {
+			return 1.0;
+		}
+		return m_sndFile->m_nFreqFactor / 65536.0;
+	} else if ( ctl == "render.resampler.emulate_amiga" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "render.opl.volume_factor" ) {
+		return static_cast<double>( m_sndFile->m_OPLVolumeFactor ) / static_cast<double>( m_sndFile->m_OPLVolumeFactorScale );
+	} else if ( ctl == "dither" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else {
+		if ( throw_if_unknown ) {
+			throw openmpt::exception("unknown ctl: " + std::string(ctl));
+		} else {
+			return 0.0;
+		}
+	}
+}
+std::string module_impl::ctl_get_text( std::string_view ctl, bool throw_if_unknown ) const {
+	if ( !ctl.empty() ) {
+		// cppcheck false-positive
+		// cppcheck-suppress containerOutOfBounds
+		char rightmost = ctl.back();
+		if ( rightmost == '!' || rightmost == '?' ) {
+			if ( rightmost == '!' ) {
+				throw_if_unknown = true;
+			} else if ( rightmost == '?' ) {
+				throw_if_unknown = false;
+			}
+			ctl = ctl.substr( 0, ctl.length() - 1 );
+		}
+	}
+	if ( ctl == "" ) {
+		throw openmpt::exception("empty ctl");
+	} else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_plugins" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_subsongs_init" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "seek.sync_samples" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "subsong" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.at_end" ) {
+		switch ( m_ctl_play_at_end )
+		{
+		case song_end_action::fadeout_song:
+			return "fadeout";
+		case song_end_action::continue_song:
+			return "continue";
+		case song_end_action::stop_song:
+			return "stop";
+		default:
+			return std::string();
+		}
+	} else if ( ctl == "play.tempo_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.pitch_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "render.resampler.emulate_amiga" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "render.opl.volume_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "dither" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else {
+		if ( throw_if_unknown ) {
+			throw openmpt::exception("unknown ctl: " + std::string(ctl));
+		} else {
+			return std::string();
+		}
+	}
+}
+
 void module_impl::ctl_set( std::string ctl, const std::string & value, bool throw_if_unknown ) {
 	if ( !ctl.empty() ) {
 		// cppcheck false-positive
@@ -1671,5 +1878,229 @@
 		}
 	}
 }
+void module_impl::ctl_set_boolean( std::string_view ctl, bool value, bool throw_if_unknown ) {
+	if ( !ctl.empty() ) {
+		// cppcheck false-positive
+		// cppcheck-suppress containerOutOfBounds
+		char rightmost = ctl.back();
+		if ( rightmost == '!' || rightmost == '?' ) {
+			if ( rightmost == '!' ) {
+				throw_if_unknown = true;
+			} else if ( rightmost == '?' ) {
+				throw_if_unknown = false;
+			}
+			ctl = ctl.substr( 0, ctl.length() - 1 );
+		}
+	}
+	if ( ctl == "" ) {
+		throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) );
+	} else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) {
+		m_ctl_load_skip_samples = value;
+	} else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) {
+		m_ctl_load_skip_patterns = value;
+	} else if ( ctl == "load.skip_plugins" ) {
+		m_ctl_load_skip_plugins = value;
+	} else if ( ctl == "load.skip_subsongs_init" ) {
+		m_ctl_load_skip_subsongs_init = value;
+	} else if ( ctl == "seek.sync_samples" ) {
+		m_ctl_seek_sync_samples = value;
+	} else if ( ctl == "subsong" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.at_end" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.tempo_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.pitch_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "render.resampler.emulate_amiga" ) {
+		CResamplerSettings newsettings = m_sndFile->m_Resampler.m_Settings;
+		newsettings.emulateAmiga = value;
+		if ( newsettings != m_sndFile->m_Resampler.m_Settings ) {
+			m_sndFile->SetResamplerSettings( newsettings );
+		}
+	} else if ( ctl == "render.opl.volume_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "dither" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else {
+		if ( throw_if_unknown ) {
+			throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + mpt::fmt::val(value));
+		} else {
+			// ignore
+		}
+	}
+}
+void module_impl::ctl_set_integer( std::string_view ctl, std::int64_t value, bool throw_if_unknown ) {
+	if ( !ctl.empty() ) {
+		// cppcheck false-positive
+		// cppcheck-suppress containerOutOfBounds
+		char rightmost = ctl.back();
+		if ( rightmost == '!' || rightmost == '?' ) {
+			if ( rightmost == '!' ) {
+				throw_if_unknown = true;
+			} else if ( rightmost == '?' ) {
+				throw_if_unknown = false;
+			}
+			ctl = ctl.substr( 0, ctl.length() - 1 );
+		}
+	}
+	if ( ctl == "" ) {
+		throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) );
+	} else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_plugins" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_subsongs_init" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "seek.sync_samples" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "subsong" ) {
+		select_subsong( mpt::saturate_cast<int32>( value ) );
+	} else if ( ctl == "play.at_end" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.tempo_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.pitch_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "render.resampler.emulate_amiga" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "render.opl.volume_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "dither" ) {
+		int dither = mpt::saturate_cast<int>( value );
+		if ( dither < 0 || dither >= NumDitherModes ) {
+			dither = DitherDefault;
+		}
+		m_Dither->SetMode( static_cast<DitherMode>( dither ) );
+	} else {
+		if ( throw_if_unknown ) {
+			throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + mpt::fmt::val(value));
+		} else {
+			// ignore
+		}
+	}
+}
+void module_impl::ctl_set_floatingpoint( std::string_view ctl, double value, bool throw_if_unknown ) {
+	if ( !ctl.empty() ) {
+		// cppcheck false-positive
+		// cppcheck-suppress containerOutOfBounds
+		char rightmost = ctl.back();
+		if ( rightmost == '!' || rightmost == '?' ) {
+			if ( rightmost == '!' ) {
+				throw_if_unknown = true;
+			} else if ( rightmost == '?' ) {
+				throw_if_unknown = false;
+			}
+			ctl = ctl.substr( 0, ctl.length() - 1 );
+		}
+	}
+	if ( ctl == "" ) {
+		throw openmpt::exception("empty ctl: := " + mpt::fmt::val( value ) );
+	} else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_plugins" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_subsongs_init" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "seek.sync_samples" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "subsong" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.at_end" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.tempo_factor" ) {
+		if ( !is_loaded() ) {
+			return;
+		}
+		double factor = value;
+		if ( factor <= 0.0 || factor > 4.0 ) {
+			throw openmpt::exception("invalid tempo factor");
+		}
+		m_sndFile->m_nTempoFactor = mpt::saturate_round<uint32_t>( 65536.0 / factor );
+		m_sndFile->RecalculateSamplesPerTick();
+	} else if ( ctl == "play.pitch_factor" ) {
+		if ( !is_loaded() ) {
+			return;
+		}
+		double factor = value;
+		if ( factor <= 0.0 || factor > 4.0 ) {
+			throw openmpt::exception("invalid pitch factor");
+		}
+		m_sndFile->m_nFreqFactor = mpt::saturate_round<uint32_t>( 65536.0 * factor );
+		m_sndFile->RecalculateSamplesPerTick();
+	} else if ( ctl == "render.resampler.emulate_amiga" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "render.opl.volume_factor" ) {
+		m_sndFile->m_OPLVolumeFactor = mpt::saturate_round<int32>( value * static_cast<double>( m_sndFile->m_OPLVolumeFactorScale ) );
+	} else if ( ctl == "dither" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else {
+		if ( throw_if_unknown ) {
+			throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + mpt::fmt::val(value));
+		} else {
+			// ignore
+		}
+	}
+}
+void module_impl::ctl_set_text( std::string_view ctl, std::string_view value, bool throw_if_unknown ) {
+	if ( !ctl.empty() ) {
+		// cppcheck false-positive
+		// cppcheck-suppress containerOutOfBounds
+		char rightmost = ctl.back();
+		if ( rightmost == '!' || rightmost == '?' ) {
+			if ( rightmost == '!' ) {
+				throw_if_unknown = true;
+			} else if ( rightmost == '?' ) {
+				throw_if_unknown = false;
+			}
+			ctl = ctl.substr( 0, ctl.length() - 1 );
+		}
+	}
+	if ( ctl == "" ) {
+		throw openmpt::exception("empty ctl: := " + std::string( value ) );
+	} else if ( ctl == "load.skip_samples" || ctl == "load_skip_samples" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_patterns" || ctl == "load_skip_patterns" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_plugins" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "load.skip_subsongs_init" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "seek.sync_samples" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "subsong" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.at_end" ) {
+		if ( value == "fadeout" ) {
+			m_ctl_play_at_end = song_end_action::fadeout_song;
+		} else if(value == "continue") {
+			m_ctl_play_at_end = song_end_action::continue_song;
+		} else if(value == "stop") {
+			m_ctl_play_at_end = song_end_action::stop_song;
+		} else {
+			throw openmpt::exception("unknown song end action:" + std::string(value));
+		}
+	} else if ( ctl == "play.tempo_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "play.pitch_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "render.resampler.emulate_amiga" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "render.opl.volume_factor" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else if ( ctl == "dither" ) {
+		throw openmpt::exception("wrong ctl value type");
+	} else {
+		if ( throw_if_unknown ) {
+			throw openmpt::exception("unknown ctl: " + std::string(ctl) + " := " + std::string(value));
+		} else {
+			// ignore
+		}
+	}
+}
 
 } // namespace openmpt
Index: libopenmpt/libopenmpt_impl.hpp
===================================================================
--- libopenmpt/libopenmpt_impl.hpp	(revision 12197)
+++ libopenmpt/libopenmpt_impl.hpp	(working copy)
@@ -130,8 +130,7 @@
 	static double could_open_probability( const OpenMPT::FileReader & file, double effort, std::unique_ptr<log_interface> log );
 public:
 	static std::vector<std::string> get_supported_extensions();
-	static bool is_extension_supported( const char * extension );
-	static bool is_extension_supported( const std::string & extension );
+	static bool is_extension_supported( std::string_view extension );
 	static double could_open_probability( callback_stream_wrapper stream, double effort, std::unique_ptr<log_interface> log );
 	static double could_open_probability( std::istream & stream, double effort, std::unique_ptr<log_interface> log );
 	static std::size_t probe_file_header_get_recommended_size();
@@ -204,7 +203,15 @@
 	std::string highlight_pattern_row_channel( std::int32_t p, std::int32_t r, std::int32_t c, std::size_t width, bool pad ) const;
 	std::vector<std::string> get_ctls() const;
 	std::string ctl_get( std::string ctl, bool throw_if_unknown = true ) const;
+	bool ctl_get_boolean( std::string_view ctl, bool throw_if_unknown = true ) const;
+	std::int64_t ctl_get_integer( std::string_view ctl, bool throw_if_unknown = true ) const;
+	double ctl_get_floatingpoint( std::string_view ctl, bool throw_if_unknown = true ) const;
+	std::string ctl_get_text( std::string_view ctl, bool throw_if_unknown = true ) const;
 	void ctl_set( std::string ctl, const std::string & value, bool throw_if_unknown = true );
+	void ctl_set_boolean( std::string_view ctl, bool value, bool throw_if_unknown = true );
+	void ctl_set_integer( std::string_view ctl, std::int64_t value, bool throw_if_unknown = true );
+	void ctl_set_floatingpoint( std::string_view ctl, double value, bool throw_if_unknown = true );
+	void ctl_set_text( std::string_view ctl, std::string_view value, bool throw_if_unknown = true );
 }; // class module_impl
 
 namespace helper {
Index: openmpt123/openmpt123.cpp
===================================================================
--- openmpt123/openmpt123.cpp	(revision 12197)
+++ openmpt123/openmpt123.cpp	(working copy)
@@ -711,33 +711,6 @@
 }
 
 
-template < typename T, typename Tmod >
-T ctl_get( Tmod & mod, const std::string & ctl ) {
-	T result = T();
-	try {
-		std::istringstream str;
-		str.imbue( std::locale::classic() );
-		str.str( mod.ctl_get( ctl ) );
-		str >> std::fixed >> std::setprecision(16) >> result;
-	} catch ( const openmpt::exception & ) {
-		// ignore
-	}
-	return result;
-}
-
-template < typename T, typename Tmod >
-void ctl_set( Tmod & mod, const std::string & ctl, const T & val ) {
-	try {
-		std::ostringstream str;
-		str.imbue( std::locale::classic() );
-		str << std::fixed << std::setprecision(16) << val;
-		mod.ctl_set( ctl, str.str() );
-	} catch ( const openmpt::exception & ) {
-		// ignore
-	}
-	return;
-}
-
 template < typename Tmod >
 static void apply_mod_settings( commandlineflags & flags, Tmod & mod ) {
 	flags.separation = std::max( flags.separation, std::int32_t(   0 ) );
@@ -753,12 +726,17 @@
 	mod.set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, flags.separation );
 	mod.set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, flags.filtertaps );
 	mod.set_render_param( openmpt::module::RENDER_VOLUMERAMPING_STRENGTH, flags.ramping );
-	ctl_set( mod, "play.tempo_factor", tempo_flag_to_double( flags.tempo ) );
-	ctl_set( mod, "play.pitch_factor", pitch_flag_to_double( flags.pitch ) );
-	std::ostringstream dither_str;
-	dither_str.imbue( std::locale::classic() );
-	dither_str << flags.dither;
-	mod.ctl_set( "dither", dither_str.str() );
+	try {
+		mod.ctl_set_floatingpoint( "play.tempo_factor", tempo_flag_to_double( flags.tempo ) );
+	} catch ( const openmpt::exception & ) {
+		// ignore
+	}
+	try {
+		mod.ctl_set_floatingpoint( "play.pitch_factor", pitch_flag_to_double( flags.pitch ) );
+	} catch ( const openmpt::exception & ) {
+		// ignore
+	}
+	mod.ctl_set_integer( "dither", flags.dither );
 }
 
 struct prev_file { int count; prev_file( int c ) : count(c) { } };
Index: soundlib/Sndfile.h
===================================================================
--- soundlib/Sndfile.h	(revision 12197)
+++ soundlib/Sndfile.h	(working copy)
@@ -823,7 +823,7 @@
 	bool ReadWAV(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
 
 	static std::vector<const char *> GetSupportedExtensions(bool otherFormats);
-	static bool IsExtensionSupported(const char *ext); // UTF8, casing of ext is ignored
+	static bool IsExtensionSupported(std::string_view ext); // UTF8, casing of ext is ignored
 	static mpt::ustring ModContainerTypeToString(MODCONTAINERTYPE containertype);
 	static mpt::ustring ModContainerTypeToTracker(MODCONTAINERTYPE containertype);
 
Index: soundlib/Tables.cpp
===================================================================
--- soundlib/Tables.cpp	(revision 12197)
+++ soundlib/Tables.cpp	(working copy)
@@ -176,21 +176,19 @@
 }
 
 
-static bool IsEqualExtension(const char *a, const char *b)
+static bool IsEqualExtension(std::string_view a, std::string_view b)
 {
-	std::size_t lena = std::strlen(a);
-	std::size_t lenb = std::strlen(b);
-	if(lena != lenb)
+	if(a.length() != b.length())
 	{
 		return false;
 	}
-	return mpt::CompareNoCaseAscii(a, b, lena) == 0;
+	return mpt::CompareNoCaseAscii(a, b) == 0;
 }
 
 
-bool CSoundFile::IsExtensionSupported(const char *ext)
+bool CSoundFile::IsExtensionSupported(std::string_view ext)
 {
-	if(ext == nullptr || ext[0] == 0)
+	if(ext.length() == 0)
 	{
 		return false;
 	}
manx

manx

2019-10-14 18:27

administrator   ~0004109

v3 also adds string_view for is_extension_supported()

Issue History

Date Modified Username Field Change
2019-07-29 20:47 manx New Issue
2019-07-29 20:47 manx Status new => assigned
2019-07-29 20:47 manx Assigned To => manx
2019-07-29 20:47 manx Relationship added related to 0001240
2019-10-03 08:41 manx Note Added: 0004102
2019-10-14 18:25 manx File Added: libopenmpt-api-cpp17-v3.patch
2019-10-14 18:27 manx Note Added: 0004109