drumstick-2.9.0/0000755000175000017500000000000014541630232012533 5ustar pedropedrodrumstick-2.9.0/cmake_admin/0000755000175000017500000000000014541630232014763 5ustar pedropedrodrumstick-2.9.0/cmake_admin/COPYING-CMAKE-SCRIPTS0000644000175000017500000000245614541630232017770 0ustar pedropedroRedistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. drumstick-2.9.0/cmake_admin/CustomBundleInfo.plist.in0000644000175000017500000000246614541630232021675 0ustar pedropedro NSPrincipalClass NSApplication NSHighResolutionCapable True CFBundleDevelopmentRegion English CFBundleExecutable ${MACOSX_BUNDLE_EXECUTABLE_NAME} CFBundleGetInfoString ${MACOSX_BUNDLE_INFO_STRING} CFBundleIconFile ${MACOSX_BUNDLE_ICON_FILE} CFBundleIdentifier ${MACOSX_BUNDLE_GUI_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString ${MACOSX_BUNDLE_LONG_VERSION_STRING} CFBundleName ${MACOSX_BUNDLE_BUNDLE_NAME} CFBundlePackageType APPL CFBundleShortVersionString ${MACOSX_BUNDLE_SHORT_VERSION_STRING} CFBundleSignature ???? CFBundleVersion ${MACOSX_BUNDLE_BUNDLE_VERSION} CSResourcesFileMapped NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} drumstick-2.9.0/cmake_admin/CustomFrameworkInfo.plist.in0000644000175000017500000000133614541630232022414 0ustar pedropedro CFBundlePackageType FMWK CFBundleShortVersionString ${PROJECT_VERSION} CFBundleVersion ${PROJECT_VERSION} CFBundleGetInfoString Created by CMake CFBundleIdentifier ${MACOSX_FRAMEWORK_IDENTIFIER} NSHumanReadableCopyright © 2006-2021, Pedro López-Cabanillas and others NOTE Please, do NOT change this file -- It was generated by CMake. drumstick-2.9.0/cmake_admin/FindSharedMimeInfo.cmake0000644000175000017500000000561514541630232021427 0ustar pedropedro# - Try to find the shared-mime-info package # # SHARED_MIME_INFO_MINIMUM_VERSION - Set this to the minimum version you need, default is 0.18 # # Once done this will define # # SHARED_MIME_INFO_FOUND - system has the shared-mime-info package # UPDATE_MIME_DATABASE_EXECUTABLE - the update-mime-database executable # Copyright (c) 2007, Pino Toscano, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # the minimum version of shared-mime-database we require if (NOT SHARED_MIME_INFO_MINIMUM_VERSION) set(SHARED_MIME_INFO_MINIMUM_VERSION "0.18") endif (NOT SHARED_MIME_INFO_MINIMUM_VERSION) if (UPDATE_MIME_DATABASE_EXECUTABLE) # in cache already set(SHARED_MIME_INFO_FOUND TRUE) else (UPDATE_MIME_DATABASE_EXECUTABLE) include (MacroEnsureVersion) find_program (UPDATE_MIME_DATABASE_EXECUTABLE NAMES update-mime-database) if (UPDATE_MIME_DATABASE_EXECUTABLE) exec_program (${UPDATE_MIME_DATABASE_EXECUTABLE} ARGS -v RETURN_VALUE _null OUTPUT_VARIABLE _smiVersionRaw) string(REGEX REPLACE "update-mime-database \\([a-zA-Z\\-]+\\) ([0-9]\\.[0-9]+).*" "\\1" smiVersion "${_smiVersionRaw}") set (SHARED_MIME_INFO_FOUND TRUE) endif (UPDATE_MIME_DATABASE_EXECUTABLE) if (SHARED_MIME_INFO_FOUND) if (NOT SharedMimeInfo_FIND_QUIETLY) message(STATUS "Found shared-mime-info version: ${smiVersion}") macro_ensure_version(${SHARED_MIME_INFO_MINIMUM_VERSION} ${smiVersion} _smiVersion_OK) if (NOT _smiVersion_OK) message(FATAL_ERROR "The found version of shared-mime-info (${smiVersion}) is below the minimum required (${SHARED_MIME_INFO_MINIMUM_VERSION})") endif (NOT _smiVersion_OK) endif (NOT SharedMimeInfo_FIND_QUIETLY) else (SHARED_MIME_INFO_FOUND) if (SharedMimeInfo_FIND_REQUIRED) message(FATAL_ERROR "Could NOT find shared-mime-info. See http://freedesktop.org/wiki/Software/shared-mime-info.") endif (SharedMimeInfo_FIND_REQUIRED) endif (SHARED_MIME_INFO_FOUND) endif (UPDATE_MIME_DATABASE_EXECUTABLE) mark_as_advanced(UPDATE_MIME_DATABASE_EXECUTABLE) macro(UPDATE_XDG_MIMETYPES _path) get_filename_component(_xdgmimeDir "${_path}" NAME) if("${_xdgmimeDir}" STREQUAL packages ) get_filename_component(_xdgmimeDir "${_path}" PATH) else("${_xdgmimeDir}" STREQUAL packages ) set(_xdgmimeDir "${_path}") endif("${_xdgmimeDir}" STREQUAL packages ) install(CODE " set(DESTDIR_VALUE \"\$ENV{DESTDIR}\") if (NOT DESTDIR_VALUE) # under Windows relative paths are used, that's why it runs from CMAKE_INSTALL_PREFIX execute_process(COMMAND ${UPDATE_MIME_DATABASE_EXECUTABLE} ${_xdgmimeDir} WORKING_DIRECTORY \"${CMAKE_INSTALL_PREFIX}\") endif (NOT DESTDIR_VALUE) ") endmacro (UPDATE_XDG_MIMETYPES) drumstick-2.9.0/cmake_admin/MacroEnsureVersion.cmake0000644000175000017500000001173514541630232021565 0ustar pedropedro# This file defines the following macros for developers to use in ensuring # that installed software is of the right version: # # MACRO_ENSURE_VERSION - test that a version number is greater than # or equal to some minimum # MACRO_ENSURE_VERSION_RANGE - test that a version number is greater than # or equal to some minimum and less than some # maximum # MACRO_ENSURE_VERSION2 - deprecated, do not use in new code # # MACRO_ENSURE_VERSION # This macro compares version numbers of the form "x.y.z" or "x.y" # MACRO_ENSURE_VERSION( FOO_MIN_VERSION FOO_VERSION_FOUND FOO_VERSION_OK) # will set FOO_VERSION_OK to true if FOO_VERSION_FOUND >= FOO_MIN_VERSION # Leading and trailing text is ok, e.g. # MACRO_ENSURE_VERSION( "2.5.31" "flex 2.5.4a" VERSION_OK) # which means 2.5.31 is required and "flex 2.5.4a" is what was found on the system # Copyright (c) 2006, David Faure, # Copyright (c) 2007, Will Stephenson # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # MACRO_ENSURE_VERSION_RANGE # This macro ensures that a version number of the form # "x.y.z" or "x.y" falls within a range defined by # min_version <= found_version < max_version. # If this expression holds, FOO_VERSION_OK will be set TRUE # # Example: MACRO_ENSURE_VERSION_RANGE3( "0.1.0" ${FOOCODE_VERSION} "0.7.0" FOO_VERSION_OK ) # # This macro will break silently if any of x,y,z are greater than 100. # # Copyright (c) 2007, Will Stephenson # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # NORMALIZE_VERSION # Helper macro to convert version numbers of the form "x.y.z" # to an integer equal to 10^4 * x + 10^2 * y + z # # This macro will break silently if any of x,y,z are greater than 100. # # Copyright (c) 2006, David Faure, # Copyright (c) 2007, Will Stephenson # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # CHECK_RANGE_INCLUSIVE_LOWER # Helper macro to check whether x <= y < z # # Copyright (c) 2007, Will Stephenson # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. MACRO(NORMALIZE_VERSION _requested_version _normalized_version) STRING(REGEX MATCH "[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" _threePartMatch "${_requested_version}") if (_threePartMatch) # parse the parts of the version string STRING(REGEX REPLACE "[^0-9]*([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" _major_vers "${_requested_version}") STRING(REGEX REPLACE "[^0-9]*[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" _minor_vers "${_requested_version}") STRING(REGEX REPLACE "[^0-9]*[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" _patch_vers "${_requested_version}") else (_threePartMatch) STRING(REGEX REPLACE "([0-9]+)\\.[0-9]+" "\\1" _major_vers "${_requested_version}") STRING(REGEX REPLACE "[0-9]+\\.([0-9]+)" "\\1" _minor_vers "${_requested_version}") set(_patch_vers "0") endif (_threePartMatch) # compute an overall version number which can be compared at once MATH(EXPR ${_normalized_version} "${_major_vers}*10000 + ${_minor_vers}*100 + ${_patch_vers}") ENDMACRO(NORMALIZE_VERSION) MACRO(MACRO_CHECK_RANGE_INCLUSIVE_LOWER _lower_limit _value _upper_limit _ok) if (${_value} LESS ${_lower_limit}) set( ${_ok} FALSE ) elseif (${_value} EQUAL ${_lower_limit}) set( ${_ok} TRUE ) elseif (${_value} EQUAL ${_upper_limit}) set( ${_ok} FALSE ) elseif (${_value} GREATER ${_upper_limit}) set( ${_ok} FALSE ) else (${_value} LESS ${_lower_limit}) set( ${_ok} TRUE ) endif (${_value} LESS ${_lower_limit}) ENDMACRO(MACRO_CHECK_RANGE_INCLUSIVE_LOWER) MACRO(MACRO_ENSURE_VERSION requested_version found_version var_too_old) NORMALIZE_VERSION( ${requested_version} req_vers_num ) NORMALIZE_VERSION( ${found_version} found_vers_num ) if (found_vers_num LESS req_vers_num) set( ${var_too_old} FALSE ) else (found_vers_num LESS req_vers_num) set( ${var_too_old} TRUE ) endif (found_vers_num LESS req_vers_num) ENDMACRO(MACRO_ENSURE_VERSION) MACRO(MACRO_ENSURE_VERSION2 requested_version2 found_version2 var_too_old2) MACRO_ENSURE_VERSION( ${requested_version2} ${found_version2} ${var_too_old2}) ENDMACRO(MACRO_ENSURE_VERSION2) MACRO(MACRO_ENSURE_VERSION_RANGE min_version found_version max_version var_ok) NORMALIZE_VERSION( ${min_version} req_vers_num ) NORMALIZE_VERSION( ${found_version} found_vers_num ) NORMALIZE_VERSION( ${max_version} max_vers_num ) MACRO_CHECK_RANGE_INCLUSIVE_LOWER( ${req_vers_num} ${found_vers_num} ${max_vers_num} ${var_ok}) ENDMACRO(MACRO_ENSURE_VERSION_RANGE) drumstick-2.9.0/cmake_admin/cmake_uninstall.cmake.in0000644000175000017500000000155514541630232021551 0ustar pedropedroIF(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) drumstick-2.9.0/cmake_admin/CreateManpages.cmake0000644000175000017500000000317314541630232020650 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] MACRO(CREATE_MANPAGES) SET(outfiles) FOREACH (it ${ARGN}) GET_FILENAME_COMPONENT(outfile ${it} NAME_WE) GET_FILENAME_COMPONENT(infile ${it} ABSOLUTE) SET(outfile ${CMAKE_CURRENT_BINARY_DIR}/${outfile}.1) SET(outfiles ${outfiles} ${outfile}) ADD_CUSTOM_COMMAND( OUTPUT ${outfile} COMMAND ${XSLTPROC_EXECUTABLE} --nonet --xinclude --xincludestyle --output ${outfile} http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl ${infile} DEPENDS ${infile}) ENDFOREACH (it) ADD_CUSTOM_TARGET(manpages ALL DEPENDS ${outfiles}) INSTALL ( FILES ${outfiles} DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" ) ENDMACRO(CREATE_MANPAGES) drumstick-2.9.0/cmake_admin/SCMRevision.cmake0000644000175000017500000000340414541630232020127 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] find_package(Subversion QUIET) if (Subversion_FOUND) Subversion_WC_INFO(${PROJECT_SOURCE_DIR} PROJECT IGNORE_SVN_FAILURE) if (DEFINED PROJECT_WC_REVISION) message(STATUS "Current revision (SVN) is ${PROJECT_WC_REVISION}") endif() endif() if (NOT DEFINED PROJECT_WC_REVISION) find_package(Git QUIET) if (Git_FOUND) execute_process( COMMAND "${GIT_EXECUTABLE}" rev-parse --short HEAD WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE PROJECT_WC_REVISION ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (${res} EQUAL 0) message(STATUS "Current revision (Git) is ${PROJECT_WC_REVISION}") else() unset(PROJECT_WC_REVISION) endif() endif() endif() if (DEFINED PROJECT_WC_REVISION) set(${PROJECT_NAME}_WC_REVISION ${PROJECT_WC_REVISION}) endif() drumstick-2.9.0/library/0000755000175000017500000000000014541630232014177 5ustar pedropedrodrumstick-2.9.0/library/Info.plist.lib0000644000175000017500000000153314541630232016716 0ustar pedropedro CFBundlePackageType FMWK CFBundleShortVersionString @FULL_VERSION@ CFBundleVersion @FULL_VERSION@ CFBundleGetInfoString Created by Qt/QMake CFBundleSignature @TYPEINFO@ CFBundleExecutable @LIBRARY@ CFBundleIdentifier @BUNDLEIDENTIFIER@ NSHumanReadableCopyright © 2006-2019, Pedro López-Cabanillas and others NOTE Please, do NOT change this file -- It was generated by Qt/QMake. drumstick-2.9.0/library/alsa/0000755000175000017500000000000014541630232015117 5ustar pedropedrodrumstick-2.9.0/library/alsa/alsa.pro0000644000175000017500000000202314541630232016556 0ustar pedropedroTEMPLATE = lib TARGET = drumstick-alsa DESTDIR = ../../build/lib DEPENDPATH += . ../include INCLUDEPATH += . ../include include (../../global.pri) QT -= gui #QT += dbus CONFIG += c++11 qt thread link_pkgconfig create_pc create_prl no_install_prl static { CONFIG += staticlib } DEFINES += drumstick_alsa_EXPORTS #RTKIT_SUPPORT QMAKE_CXXFLAGS += $$QMAKE_CXXFLAGS_HIDESYMS QMAKE_PKGCONFIG_PREFIX = $$INSTALLBASE # Input HEADERS += \ ../include/drumstick.h \ ../include/drumstick/alsaclient.h \ ../include/drumstick/alsaevent.h \ ../include/drumstick/alsaport.h \ ../include/drumstick/alsaqueue.h \ ../include/drumstick/alsatimer.h \ ../include/drumstick/macros.h \ ../include/drumstick/playthread.h \ ../include/drumstick/subscription.h \ ../include/drumstick/sequencererror.h \ errorcheck.h SOURCES += \ alsaclient.cpp \ alsaevent.cpp \ alsaport.cpp \ alsaqueue.cpp \ alsatimer.cpp \ playthread.cpp \ sequencererror.cpp \ subscription.cpp linux:PKGCONFIG += alsa drumstick-2.9.0/library/alsa/alsaqueue.cpp0000644000175000017500000005167614541630232017627 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "errorcheck.h" #include #include #include #include #include /** * @file alsaqueue.cpp * Implementation of classes managing ALSA Sequencer queues */ namespace drumstick { namespace ALSA { /** * This is the value for the base skew used in ALSA. It is not possible * to assign an arbitrary value (ALSA version <= 1.0.20). */ const unsigned int SKEW_BASE = 0x10000; /** * @addtogroup ALSAQueue * @{ * * ALSA events can be delivered to the output ports at scheduled times using the * queues. There is a small amount of available queues in the system, so this is * a limited resource. Queues are also used to time-stamp incoming events. * * Classes: * * QueueInfo holds several properties about the queue object. * * QueueStatus is used to retrieve the status of the queue object. * * QueueTempo holds properties to get and set the tempo in the queue object. * * QueueTimer holds properties about the Timer used in the queue object. * * MidiQueue represents the queue object. * * @see https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_queue.html * @} */ /** * Default constructor */ QueueInfo::QueueInfo() { snd_seq_queue_info_malloc(&m_Info); } /** * Constructor. * @param other ALSA queue info object pointer */ QueueInfo::QueueInfo(snd_seq_queue_info_t* other) { snd_seq_queue_info_malloc(&m_Info); snd_seq_queue_info_copy(m_Info, other); } /** * Copy constructor. * @param other An existing QueueInfo object reference. */ QueueInfo::QueueInfo(const QueueInfo& other) { snd_seq_queue_info_malloc(&m_Info); snd_seq_queue_info_copy(m_Info, other.m_Info); } /** * Destructor */ QueueInfo::~QueueInfo() { snd_seq_queue_info_free(m_Info); } /** * Copy the current object and return the copy. * @return The pointer to the new object. */ QueueInfo* QueueInfo::clone() { return new QueueInfo(m_Info); } /** * Assignment operator. * @param other An existing QueueInfo object reference. * @return This object. */ QueueInfo& QueueInfo::operator=(const QueueInfo& other) { if (this == &other) return *this; snd_seq_queue_info_copy(m_Info, other.m_Info); return *this; } /** * Gets the queue's numeric identifier. * @return The numeric identifier. */ int QueueInfo::getId() { return snd_seq_queue_info_get_queue(m_Info); } /** * Gets the queue name * @return The queue name. */ QString QueueInfo::getName() { return QString(snd_seq_queue_info_get_name(m_Info)); } /** * Gets the owner's client id of the queue. * @return the owner's client id. */ int QueueInfo::getOwner() { return snd_seq_queue_info_get_owner(m_Info); } /** * Returns the locking status of the queue * @return The locking status. */ bool QueueInfo::isLocked() { return (snd_seq_queue_info_get_locked(m_Info) != 0); } /** * Gets the flags of the queue. * @return The flags of the queue. */ unsigned int QueueInfo::getFlags() { return snd_seq_queue_info_get_flags(m_Info); } /** * Sets the queue name * @param value The queue name */ void QueueInfo::setName(QString value) { snd_seq_queue_info_set_name(m_Info, value.toLocal8Bit().data()); } /** * Sets the client ID of the owner * @param value The client ID of the owner */ void QueueInfo::setOwner(int value) { snd_seq_queue_info_set_owner(m_Info, value); } /** * Sets the bit flags of the queue * @param value The bit flags */ void QueueInfo::setFlags(unsigned int value) { snd_seq_queue_info_set_flags(m_Info, value); } /** * Sets the locked status of the queue * @param locked The locked status */ void QueueInfo::setLocked(bool locked) { snd_seq_queue_info_set_locked(m_Info, locked ? 1 : 0); } /** * Gets the size of the ALSA queue info object. * @return The size of the ALSA object. */ int QueueInfo::getInfoSize() const { return snd_seq_queue_info_sizeof(); } /** * Default constructor */ QueueStatus::QueueStatus() { snd_seq_queue_status_malloc(&m_Info); } /** * Constructor * @param other ALSA queue status object pointer */ QueueStatus::QueueStatus(snd_seq_queue_status_t* other) { snd_seq_queue_status_malloc(&m_Info); snd_seq_queue_status_copy(m_Info, other); } /** * Copy constructor * @param other An existing QueueStatus object reference */ QueueStatus::QueueStatus(const QueueStatus& other) { snd_seq_queue_status_malloc(&m_Info); snd_seq_queue_status_copy(m_Info, other.m_Info); } /** * Destructor */ QueueStatus::~QueueStatus() { snd_seq_queue_status_free(m_Info); } /** * Copy the current object and return the copy * @return The pointer to the new object */ QueueStatus* QueueStatus::clone() { return new QueueStatus(m_Info); } /** * Assignment operator * @param other An existing QueueStatus object reference * @return This object */ QueueStatus& QueueStatus::operator=(const QueueStatus& other) { if (this == &other) return *this; snd_seq_queue_status_copy(m_Info, other.m_Info); return *this; } /** * Gets the queue's numeric identifier * @return The queue's numeric identifier. */ int QueueStatus::getId() { return snd_seq_queue_status_get_queue(m_Info); } /** * Gets the number of queued events * @return The number of queued events */ int QueueStatus::getEvents() { return snd_seq_queue_status_get_events(m_Info); } /** * Gets the real time (secods and nanoseconds) of the queue * @return The queue's real time. */ const snd_seq_real_time_t* QueueStatus::getRealtime() { return snd_seq_queue_status_get_real_time(m_Info); } /** * Gets the running status bits * @return The running status bits */ unsigned int QueueStatus::getStatusBits() { return snd_seq_queue_status_get_status(m_Info); } /** * Gets the musical time (ticks) of the queue * @return The musical time */ snd_seq_tick_time_t QueueStatus::getTickTime() { return snd_seq_queue_status_get_tick_time(m_Info); } /** * Gets the size of the ALSA status object * @return The size of the ALSA object */ int QueueStatus::getInfoSize() const { return snd_seq_queue_status_sizeof(); } /** * Gets the queue's running state * @return True if the queue is running */ bool QueueStatus::isRunning() { return (snd_seq_queue_status_get_status(m_Info) != 0); } /** * Gets the clock time in seconds of the queue * @return The queue time in seconds */ double QueueStatus::getClockTime() { const snd_seq_real_time_t* time = snd_seq_queue_status_get_real_time(m_Info); return (time->tv_sec * 1.0) + (time->tv_nsec * 1.0e-9); } /** * Default constructor */ QueueTempo::QueueTempo() { snd_seq_queue_tempo_malloc(&m_Info); } /** * Constructor * @param other An ALSA queue tempo object pointer */ QueueTempo::QueueTempo(snd_seq_queue_tempo_t* other) { snd_seq_queue_tempo_malloc(&m_Info); snd_seq_queue_tempo_copy(m_Info, other); } /** * Copy constructor * @param other An existing QueueTempo object reference */ QueueTempo::QueueTempo(const QueueTempo& other) { snd_seq_queue_tempo_malloc(&m_Info); snd_seq_queue_tempo_copy(m_Info, other.m_Info); } /** * Destructor */ QueueTempo::~QueueTempo() { snd_seq_queue_tempo_free(m_Info); } /** * Copy the current object returning the copied object * @return The pointer to the new object */ QueueTempo* QueueTempo::clone() { return new QueueTempo(m_Info); } /** * Assignment operator * @param other An existing QueueTempo object reference * @return This object */ QueueTempo& QueueTempo::operator=(const QueueTempo& other) { if (this == &other) return *this; snd_seq_queue_tempo_copy(m_Info, other.m_Info); return *this; } /** * Gets the queue's numeric identifier * @return The queue's numeric identifier */ int QueueTempo::getId() { return snd_seq_queue_tempo_get_queue(m_Info); } /** * Gets the PPQ (parts per quarter note) resolution of the queue * @return The PPQ (parts per quarter note) resolution */ int QueueTempo::getPPQ() { return snd_seq_queue_tempo_get_ppq(m_Info); } /** * Gets the tempo skew numerator. The real skew factor is the quotient of this * value divided by the skew base. * @return The tempo skew numerator. * @see getSkewBase(), setSkewValue(), setTempoFactor() */ unsigned int QueueTempo::getSkewValue() { return snd_seq_queue_tempo_get_skew(m_Info); } /** * Gets the tempo skew base. The real skew factor is the quotient of the skew * value divided by the skew base. * @return The tempo skew base. * @see getSkewValue(), setSkewValue(), setTempoFactor() */ unsigned int QueueTempo::getSkewBase() { return snd_seq_queue_tempo_get_skew_base(m_Info); } /** * Gets the queue's tempo in microseconds per beat. * @return The queue's tempo in microseconds per beat. */ unsigned int QueueTempo::getTempo() { return snd_seq_queue_tempo_get_tempo(m_Info); } /** * Sets the queue resolution in parts per quarter note. * @param value The queue resolution in PPQ. */ void QueueTempo::setPPQ(int value) { snd_seq_queue_tempo_set_ppq(m_Info, value); } /** * Sets the tempo skew numerator. The real skew factor is the quotient of this * value divided by the skew base. * @param value The tempo skew numerator. * @see getSkewBase(), getSkewValue(), setTempoFactor() */ void QueueTempo::setSkewValue(unsigned int value) { snd_seq_queue_tempo_set_skew(m_Info, value); } /** * Sets the tempo skew base. The real skew factor is the quotient of the skew * value divided by the skew base. * @bug Protected because ALSA only accepts as argument a constant SKEW_BASE * @param value The tempo skew base. * @see getSkewBase(), getSkewValue(), setTempoFactor() */ void QueueTempo::setSkewBase(unsigned int value) { snd_seq_queue_tempo_set_skew_base(m_Info, value); } /** * Sets the queue tempo in microseconds per beat * @param value The tempo in microseconds per beat */ void QueueTempo::setTempo(unsigned int value) { snd_seq_queue_tempo_set_tempo(m_Info, value); } /** * Gets the queue's nominal BPM tempo (in beats per minute) * @return The queue's nominal BPM tempo (in beats per minute) */ float QueueTempo::getNominalBPM() { int itempo = getTempo(); if (itempo != 0) return 6.0e7f / itempo; return 0.0f; } /** * Gets the queue's real BPM tempo in beats per minute. The result is equal to * the nominal BPM tempo multiplied by the skew factor. * @return */ float QueueTempo::getRealBPM() { float tempo = getNominalBPM(); return tempo * getSkewValue() / SKEW_BASE; } /** * Sets the queue's tempo skew factor * @param value The tempo skew factor. */ void QueueTempo::setTempoFactor(float value) { setSkewValue(floor(SKEW_BASE * value)); setSkewBase(SKEW_BASE); } /** * Sets the queue's nominal tempo in BPM (beats per minute). * @param value The nominal tempo in BPM (beats per minute). */ void QueueTempo::setNominalBPM(float value) { setTempo(floor(6.0e7f / value)); } /** * Gets the size of the ALSA queue tempo object * @return The size of the ALSA object */ int QueueTempo::getInfoSize() const { return snd_seq_queue_tempo_sizeof(); } /** * Default constructor */ QueueTimer::QueueTimer() { snd_seq_queue_timer_malloc(&m_Info); } /** * Constructor * @param other An ALSA queue timer object pointer */ QueueTimer::QueueTimer(snd_seq_queue_timer_t* other) { snd_seq_queue_timer_malloc(&m_Info); snd_seq_queue_timer_copy(m_Info, other); } /** * Copy constructor * @param other An existing QueueTimer object reference */ QueueTimer::QueueTimer(const QueueTimer& other) { snd_seq_queue_timer_malloc(&m_Info); snd_seq_queue_timer_copy(m_Info, other.m_Info); } /** * Destructor */ QueueTimer::~QueueTimer() { snd_seq_queue_timer_free(m_Info); } /** * Copy the current object and return the copy * @return The pointer to the new object */ QueueTimer* QueueTimer::clone() { return new QueueTimer(m_Info); } /** * Assignment operator * @param other An existing QueueTimer object reference * @return This object */ QueueTimer& QueueTimer::operator=(const QueueTimer& other) { if (this == &other) return *this; snd_seq_queue_timer_copy(m_Info, other.m_Info); return *this; } /** * The queue's numeric identifier * @return The queue's numeric identifier */ int QueueTimer::getQueueId() { return snd_seq_queue_timer_get_queue(m_Info); } /** * Gets the timer type. * * The timer type can be one of the following constants: *
    *
  • SND_SEQ_TIMER_ALSA: ALSA timer
  • *
  • SND_SEQ_TIMER_MIDI_CLOCK: MIDI Clock (CLOCK event)
  • *
  • SND_SEQ_TIMER_MIDI_TICK: MIDI Timer Tick (TICK event)
  • *
* @return the timer type. * @see setType() */ snd_seq_queue_timer_type_t QueueTimer::getType() { return snd_seq_queue_timer_get_type(m_Info); } /** * Gets the timer identifier record * @return The timer identifier record pointer */ const snd_timer_id_t* QueueTimer::getId() { return snd_seq_queue_timer_get_id(m_Info); } /** * Gets the timer resolution * @return The timer resolution */ unsigned int QueueTimer::getResolution() { return snd_seq_queue_timer_get_resolution(m_Info); } /** * Sets the timer type. * The timer type can be one of the following constants: *
    *
  • SND_SEQ_TIMER_ALSA: ALSA timer
  • *
  • SND_SEQ_TIMER_MIDI_CLOCK: MIDI Clock (CLOCK event)
  • *
  • SND_SEQ_TIMER_MIDI_TICK: MIDI Timer Tick (TICK event)
  • *
* @param value The timer type * @see getType() */ void QueueTimer::setType(snd_seq_queue_timer_type_t value) { snd_seq_queue_timer_set_type(m_Info, value); } /** * Sets the timer identifier record * @param value The timer identifier record pointer */ void QueueTimer::setId(snd_timer_id_t* value) { snd_seq_queue_timer_set_id(m_Info, value); } /** * Sets the timer identifier * @param id Timer identifier object * @since 0.3.0 */ void QueueTimer::setId(const TimerId& id) { setId(id.m_Info); } /** * Sets the timer resolution * @param value The timer resolution */ void QueueTimer::setResolution(unsigned int value) { snd_seq_queue_timer_set_resolution(m_Info, value); } /** * Gets the size of the ALSA queue timer object * @return The size of the ALSA object */ int QueueTimer::getInfoSize() const { return snd_seq_queue_timer_sizeof(); } /** * Constructor * @param seq An existing MidiClient instance * @param parent An optional parent object */ MidiQueue::MidiQueue(MidiClient* seq, QObject* parent) : QObject(parent) { m_MidiClient = seq; m_Id = DRUMSTICK_ALSA_CHECK_ERROR(snd_seq_alloc_queue(m_MidiClient->getHandle())); m_allocated = !(m_Id < 0); } /** * Constructor * @param seq An existing MidiClient instance * @param info A QueueInfo object reference * @param parent An optional parent object */ MidiQueue::MidiQueue(MidiClient* seq, const QueueInfo& info, QObject* parent) : QObject(parent) { m_MidiClient = seq; m_Info = info; m_Id = DRUMSTICK_ALSA_CHECK_ERROR(snd_seq_create_queue(m_MidiClient->getHandle(), m_Info.m_Info)); m_allocated = !(m_Id < 0); } /** * Constructor * @param seq An existing MidiClient instance * @param name The name for the new queue * @param parent An optional parent object */ MidiQueue::MidiQueue(MidiClient* seq, const QString name, QObject* parent) : QObject(parent) { m_MidiClient = seq; m_Id = DRUMSTICK_ALSA_CHECK_ERROR(snd_seq_alloc_named_queue(m_MidiClient->getHandle(), name.toLocal8Bit().data())); m_allocated = !(m_Id < 0); } /** * Constructor. * * Note: this constructor doesn't allocate a new queue, it uses an existing one. * @param seq An existing MidiClient instance * @param queue_id An existing queue numeric identifier * @param parent An optional parent object */ MidiQueue::MidiQueue(MidiClient* seq, const int queue_id, QObject* parent) : QObject(parent) { m_MidiClient = seq; m_Id = queue_id; m_allocated = false; } /** * Destructor */ MidiQueue::~MidiQueue() { if ( m_allocated && (m_MidiClient->getHandle() != nullptr) ) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_free_queue(m_MidiClient->getHandle(), m_Id)); } } /** * Gets a QueueInfo object reference * @return A QueueInfo object reference */ QueueInfo& MidiQueue::getInfo() { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_get_queue_info(m_MidiClient->getHandle(), m_Id, m_Info.m_Info)); return m_Info; } /** * Gets a QueueStatus object reference * @return A QueueStatus object reference */ QueueStatus& MidiQueue::getStatus() { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_get_queue_status(m_MidiClient->getHandle(), m_Id, m_Status.m_Info)); return m_Status; } /** * Gets a QueueTempo object reference * @return A QueueTempo object reference */ QueueTempo& MidiQueue::getTempo() { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_get_queue_tempo(m_MidiClient->getHandle(), m_Id, m_Tempo.m_Info)); return m_Tempo; } /** * Gets a QueueTimer object reference * @return A QueueTimer object reference */ QueueTimer& MidiQueue::getTimer() { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_get_queue_timer(m_MidiClient->getHandle(), m_Id, m_Timer.m_Info)); return m_Timer; } /** * Applies a QueueInfo object to the queue * @param value A QueueInfo object reference */ void MidiQueue::setInfo(const QueueInfo& value) { m_Info = value; DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_queue_info(m_MidiClient->getHandle(), m_Id, m_Info.m_Info)); } /** * Applies a QueueTempo object to the queue * @param value A QueueTempo object reference */ void MidiQueue::setTempo(const QueueTempo& value) { m_Tempo = value; DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_queue_tempo(m_MidiClient->getHandle(), m_Id, m_Tempo.m_Info)); } /** * Applies q QueueTimer object to the queue * @param value A QueueTimer object reference */ void MidiQueue::setTimer(const QueueTimer& value) { m_Timer = value; DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_queue_timer(m_MidiClient->getHandle(), m_Id, m_Timer.m_Info)); } /** * Gets the queue usage flag. * * @return 1 = client is allowed to access the queue, 0 = not allowed. */ int MidiQueue::getUsage() { return DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_get_queue_usage(m_MidiClient->getHandle(), m_Id)); } /** * Sets the queue usage flag. * * @param used 1 = client is allowed to access the queue, 0 = not allowed. */ void MidiQueue::setUsage(int used) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_queue_usage(m_MidiClient->getHandle(), m_Id, used)); } /** * Start the queue. * * This method should start running the queue from the initial position. */ void MidiQueue::start() { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_start_queue(m_MidiClient->getHandle(), m_Id, nullptr)); DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_drain_output(m_MidiClient->getHandle())); } /** * Stop the queue. * * This method should stop running the queue. */ void MidiQueue::stop() { if (m_MidiClient != nullptr && m_MidiClient->getHandle() != nullptr) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_stop_queue(m_MidiClient->getHandle(), m_Id, nullptr)); DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_drain_output(m_MidiClient->getHandle())); } } /** * Start the queue without resetting the last position. * * This method should start running the queue from the last position set. */ void MidiQueue::continueRunning() { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_continue_queue(m_MidiClient->getHandle(), m_Id, nullptr)); DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_drain_output(m_MidiClient->getHandle())); } /** * Clear the queue, dropping any scheduled events. */ void MidiQueue::clear() { if (m_MidiClient != nullptr && m_MidiClient->getHandle() != nullptr) snd_seq_drop_output(m_MidiClient->getHandle()); } /** * Sets the queue position in musical time (ticks). * @param pos Musical time in ticks. */ void MidiQueue::setTickPosition(snd_seq_tick_time_t pos) { SystemEvent event(SND_SEQ_EVENT_SETPOS_TICK); snd_seq_ev_set_queue_pos_tick(event.getHandle(), m_Id, pos); event.setDirect(); m_MidiClient->outputDirect(&event); } /** * Sets the queue position in real time (clock) units: seconds and nanoseconds. * @param pos Real time (clock) position in seconds/nanoseconds. */ void MidiQueue::setRealTimePosition(snd_seq_real_time_t* pos) { SystemEvent event(SND_SEQ_EVENT_SETPOS_TIME); snd_seq_ev_set_queue_pos_real(event.getHandle(), m_Id, pos); event.setDirect(); m_MidiClient->outputDirect(&event); } } // namespace ALSA } // namespace drumstick drumstick-2.9.0/library/alsa/drumstick-alsa-config.cmake0000644000175000017500000000020114541630232022300 0ustar pedropedroinclude(CMakeFindDependencyMacro) find_dependency(ALSA REQUIRED) include(${CMAKE_CURRENT_LIST_DIR}/drumstick-alsa-targets.cmake) drumstick-2.9.0/library/alsa/errorcheck.h0000644000175000017500000000450114541630232017417 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ERRORCHECK_H #define ERRORCHECK_H extern "C" { #include } #include #include #include /** * @file errorcheck.h * Error checking functions and macros */ namespace drumstick { namespace ALSA { /** * @addtogroup ALSAError * @{ */ /** * Checks the error code for severe errors. * If the provided error code is less than zero an exception is thrown, * containing both the error code and the location. * @param rc Error code * @param where Location * @return Error code */ inline int checkErrorAndThrow(int rc, const char *where) { if (rc < 0) { qDebug() << "Error code:" << rc << "(" << snd_strerror(rc) << ")"; qDebug() << "Location:" << where; throw SequencerError(QString(where), rc); } return rc; } /** * Check the error code for warning errors. * This method doesn't throw an exception. * @param rc Error code * @param where Location * @return Error code */ inline int checkWarning(int rc, const char *where) { if (rc < 0) { qWarning() << "Exception code:" << rc << "(" << snd_strerror(rc) << ")"; qWarning() << "Location:" << where; } return rc; } /** * This macro calls the check error function. * @param x Error code */ #define DRUMSTICK_ALSA_CHECK_ERROR(x) (checkErrorAndThrow((x),__PRETTY_FUNCTION__)) /** * This macro calls the check warning function. * @param x Error code */ #define DRUMSTICK_ALSA_CHECK_WARNING(x) (checkWarning((x),__PRETTY_FUNCTION__)) /** @} */ }} // namespace drumstick::ALSA #endif // ERRORCHECK_H drumstick-2.9.0/library/alsa/sequencererror.cpp0000644000175000017500000000267114541630232020675 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ extern "C" { #include } #include /** * @file sequencererror.cpp * SequencerError Exception class implementation */ namespace drumstick { namespace ALSA { /** * @addtogroup ALSAError * @{ */ SequencerError::SequencerError(QString const& s, int rc): m_location(s), m_errCode(rc) { } const char *SequencerError::what() const noexcept { return snd_strerror(m_errCode); } QString SequencerError::qstrError() const { return QString(what()); } int SequencerError::code() const { return m_errCode; } const QString &SequencerError::location() const { return m_location; } /** @} */ } // namespace ALSA } // namespace drumstick drumstick-2.9.0/library/alsa/subscription.cpp0000644000175000017500000002653214541630232020357 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "errorcheck.h" #include /** * @file subscription.cpp * Implementation of classes managing ALSA sequencer subscriptions */ /** @ingroup ALSAGroup */ namespace drumstick { namespace ALSA { /** * @addtogroup ALSASubs * @{ * * Subscriptions are virtual MIDI cables between readable and writable ports. * * The ALSA sequencer readable ports are equivalent to the MIDI OUT ports in the * real world. Similarly, the writable ports are equivalent to the MIDI IN ones. * Subscriptions, like real MIDI cables, always involve a readable port (source) * and a writable port (destination). * * Classes: * * Subscriber: This class is used to enumerate the subscribers of a given (root) port. * * Subscription: This class represents a connection between two ports. * * @see https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_subscribe.html * @} */ /** * Default constructor */ Subscriber::Subscriber() { snd_seq_query_subscribe_malloc(&m_Info); } /** * Copy constructor * @param other Existing Subscriber object reference */ Subscriber::Subscriber(const Subscriber& other) { snd_seq_query_subscribe_malloc(&m_Info); snd_seq_query_subscribe_copy(m_Info, other.m_Info); } /** * Constructor * @param other Pointer to an ALSA query subscribe object */ Subscriber::Subscriber(snd_seq_query_subscribe_t* other) { snd_seq_query_subscribe_malloc(&m_Info); snd_seq_query_subscribe_copy(m_Info, other); } /** * Destructor */ Subscriber::~Subscriber() { snd_seq_query_subscribe_free(m_Info); } /** * Copy the current object * @return Pointer to the new object */ Subscriber* Subscriber::clone() { return new Subscriber(m_Info); } /** * Assignment operator * @param other Existing Subscriber object reference * @return This object */ Subscriber& Subscriber::operator=(const Subscriber& other) { if (this == &other) return *this; snd_seq_query_subscribe_copy(m_Info, other.m_Info); return *this; } /** * Gets the subscriber's client number * @return Client number */ int Subscriber::getClient() { return snd_seq_query_subscribe_get_client(m_Info); } /** * Gets the subscriober's port number * @return Port number */ int Subscriber::getPort() { return snd_seq_query_subscribe_get_port(m_Info); } /** * Gets the subscriber's root address * @return Pointer to the ALSA client/port address */ const snd_seq_addr_t* Subscriber::getRoot() { return snd_seq_query_subscribe_get_root(m_Info); } /** * Gets the subscription type (read or write). *
    *
  • SND_SEQ_QUERY_SUBS_READ: read subscriptions
  • *
  • SND_SEQ_QUERY_SUBS_WRITE: write subscriptions
  • *
* @return Subscription type */ snd_seq_query_subs_type_t Subscriber::getType() { return snd_seq_query_subscribe_get_type(m_Info); } /** * Gets the index of the subscriber container * @return Index of the subscriber */ int Subscriber::getIndex() { return snd_seq_query_subscribe_get_index(m_Info); } /** * Gets the number of subscribers returned by a query operation * @return Number of subscribers */ int Subscriber::getNumSubs() { return snd_seq_query_subscribe_get_num_subs(m_Info); } /** * Gets the subscriber's address * @return Pointer to the ALSA address record */ const snd_seq_addr_t* Subscriber::getAddr() { return snd_seq_query_subscribe_get_addr(m_Info); } /** * Gets the subscriber's queue number * @return Queue number */ int Subscriber::getQueue() { return snd_seq_query_subscribe_get_queue(m_Info); } /** * Gets the subscriber's exclusive flag * @return Exclusive flag */ bool Subscriber::getExclusive() { return (snd_seq_query_subscribe_get_exclusive(m_Info) != 0); } /** * Gets the susbcriber's time-update flag * @return Time update flag */ bool Subscriber::getTimeUpdate() { return (snd_seq_query_subscribe_get_time_update(m_Info) != 0); } /** * Gets the subscriber's time real time-stamp flag * @return Time real flag */ bool Subscriber::getTimeReal() { return (snd_seq_query_subscribe_get_time_real(m_Info) != 0); } /** * Sets the subscriber's client number * @param client Client number */ void Subscriber::setClient(int client) { snd_seq_query_subscribe_set_client(m_Info, client); } /** * Sets the subscriber's port number * @param port Port number */ void Subscriber::setPort(int port) { snd_seq_query_subscribe_set_port(m_Info, port); } /** * Sets the subscriber's root address * @param addr Pointer to the root ALSA address record */ void Subscriber::setRoot(snd_seq_addr_t* addr) { snd_seq_query_subscribe_set_root(m_Info, addr); } /** * Sets the subscription type *
    *
  • SND_SEQ_QUERY_SUBS_READ: read subscriptions
  • *
  • SND_SEQ_QUERY_SUBS_WRITE: write subscriptions
  • *
* @param type Subscription type */ void Subscriber::setType(snd_seq_query_subs_type_t type) { snd_seq_query_subscribe_set_type(m_Info, type); } /** * Sets the index of the subscriber * @param index Subscriber index */ void Subscriber::setIndex(int index) { snd_seq_query_subscribe_set_index(m_Info, index); } /** * Gets the size of the ALSA query subscriber object * @return Size of the ALSA object */ int Subscriber::getSizeOfInfo() const { return snd_seq_query_subscribe_sizeof(); } /** * Default constructor */ Subscription::Subscription() { snd_seq_port_subscribe_malloc(&m_Info); } /** * Copy constructor * @param other Existing Subscription object reference */ Subscription::Subscription(const Subscription& other) { snd_seq_port_subscribe_malloc(&m_Info); snd_seq_port_subscribe_copy(m_Info, other.m_Info); } /** * Constructor * @param other Pointer to an ALSA subscription object */ Subscription::Subscription(snd_seq_port_subscribe_t* other) { snd_seq_port_subscribe_malloc(&m_Info); snd_seq_port_subscribe_copy(m_Info, other); } /** * Constructor * @param seq Pointer to a MIDI Client object */ Subscription::Subscription(MidiClient* seq) { snd_seq_port_subscribe_malloc(&m_Info); DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_get_port_subscription(seq->getHandle(), m_Info)); } /** * Destructor */ Subscription::~Subscription() { snd_seq_port_subscribe_free(m_Info); } /** * Copy the current object * @return Pointer to the new object */ Subscription* Subscription::clone() { return new Subscription(m_Info); } /** * Assignment operator * @param other Existing subscription object reference * @return This object */ Subscription& Subscription::operator=(const Subscription& other) { if (this == &other) return *this; snd_seq_port_subscribe_copy(m_Info, other.m_Info); return *this; } /** * Gets the sender address of the subscription (MIDI OUT port) * @return Pointer to the sender ALSA address record */ const snd_seq_addr_t* Subscription::getSender() { return snd_seq_port_subscribe_get_sender(m_Info); } /** * Gets the destination address of the subscription (MIDI IN port) * @return Pointer to the destination ALSA address record */ const snd_seq_addr_t* Subscription::getDest() { return snd_seq_port_subscribe_get_dest(m_Info); } /** * Gets the susbcription's queue number * @return Queue number */ int Subscription::getQueue() { return snd_seq_port_subscribe_get_queue(m_Info); } /** * Gets the subscription's exclusive flag * @return Exclusive flag */ bool Subscription::getExclusive() { return (snd_seq_port_subscribe_get_exclusive(m_Info) != 0); } /** * Gets the susbcription's time-update flag * @return Time-update flag */ bool Subscription::getTimeUpdate() { return (snd_seq_port_subscribe_get_time_update(m_Info) != 0); } /** * Gets the susbcription's time-real (time-stamping) flag * @return Time real flag */ bool Subscription::getTimeReal() { return (snd_seq_port_subscribe_get_time_real(m_Info) != 0); } /** * Sets the Subscription's sender (MIDI OUT) port * @param addr Pointer to the sender ALSA address record */ void Subscription::setSender(const snd_seq_addr_t* addr) { snd_seq_port_subscribe_set_sender(m_Info, addr); } /** * Sets the Subscription's destination (MIDI IN) port * @param addr Pointer to the destination ALSA address record */ void Subscription::setDest(const snd_seq_addr_t* addr) { snd_seq_port_subscribe_set_dest(m_Info, addr); } /** * Sets the Subscription's Queue number * @param q Queue number */ void Subscription::setQueue(int q) { snd_seq_port_subscribe_set_queue(m_Info, q); } /** * Sets the subscription's exclusive flag * @param val Exclusive flag */ void Subscription::setExclusive(bool val) { snd_seq_port_subscribe_set_exclusive(m_Info, val?1:0); } /** * Sets the susbcription's time-update flag * @param val Time update flag */ void Subscription::setTimeUpdate(bool val) { snd_seq_port_subscribe_set_time_update(m_Info, val?1:0); } /** * Sets the subscription's time real (time-stamping) flag * @param val Time real flag */ void Subscription::setTimeReal(bool val) { snd_seq_port_subscribe_set_time_real(m_Info, val?1:0); } /** * Sets the Subscription's sender (MIDI OUT) port * @param client Client number * @param port Port number */ void Subscription::setSender(unsigned char client, unsigned char port) { snd_seq_addr_t addr; addr.client = client; addr.port = port; setSender(&addr); } /** * Sets the Subscription's destination (MIDI IN) port * @param client Client number * @param port Port number */ void Subscription::setDest(unsigned char client, unsigned char port) { snd_seq_addr_t addr; addr.client = client; addr.port = port; setDest(&addr); } /** * Performs the subscription in the ALSA sequencer subsystem. * Neither the sender nor the destination ports need to belong to the * same MidiClient instance performing the subscription. * @param seq MidiClient instance pointer */ void Subscription::subscribe(MidiClient* seq) { if ((m_Info == nullptr) || (seq == nullptr) || !(seq->isOpened())) { return; } DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_subscribe_port(seq->getHandle(), m_Info)); } /** * Breaks the subscription in the ALSA sequencer subsystem. * Neither the sender nor the destination ports need to belong to the * same MidiClient instance breaking the subscription. * @param seq MidiClient instance pointer */ void Subscription::unsubscribe(MidiClient* seq) { if ((m_Info == nullptr) || (seq == nullptr) || !(seq->isOpened())) { return; } DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_unsubscribe_port(seq->getHandle(), m_Info)); } /** * Gets the size of the ALSA subscription object * @return Size of the ALSA object */ int Subscription::getSizeOfInfo() const { return snd_seq_port_subscribe_sizeof(); } } // namespace ALSA } // namespace drumstick drumstick-2.9.0/library/alsa/alsaevent.cpp0000644000175000017500000007256514541630232017624 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "errorcheck.h" #include /** * @file alsaevent.cpp * Implementation of classes managing ALSA Sequencer events. */ /** * @class QEvent * The QEvent class is the base class of all event classes. * @see https://doc.qt.io/qt-5/qevent.html */ namespace drumstick { namespace ALSA { /** * @addtogroup ALSAEvent * @{ * * MIDI Events are messages transmitted between MIDI devices or applications. * * Classes: * * SequencerEvent: Base class for the event's hierarchy. * * ChannelEvent: Base class for the events having a Channel property. * * KeyEvent: Base class for the events having Key and Velocity properties. * * NoteEvent: Class representing a note event with duration. * * NoteOnEvent: Event representing a note-on MIDI event. * * NoteOffEvent: Event representing a note-off MIDI event. * * KeyPressEvent: Event representing a MIDI key pressure, or polyphonic after-touch event. * * ControllerEvent: Event representing a MIDI control change event. * * ProgramChangeEvent: Event representing a MIDI program change event. * * PitchBendEvent: Event representing a MIDI bender, or pitch wheel event. * * ChanPressEvent: Event representing a MIDI channel pressure or after-touch event. * * VariableEvent: Base class for variable length events. * * SysExEvent: Event representing a MIDI system exclusive event. * * TextEvent: Event representing a SMF text event. * * SystemEvent: Generic event. * * QueueControlEvent: ALSA Event representing a queue control command. * * ValueEvent: Generic event having a value property. * * TempoEvent: ALSA Event representing a tempo change for an ALSA queue. * * SubscriptionEvent: ALSA Event representing a subscription between two ALSA clients and ports. * * ClientEvent: ALSA Event representing a change on some ALSA sequencer client. * * PortEvent: ALSA Event representing a change on some ALSA sequencer port. * * RemoveEvents: Auxiliary class to remove events from an ALSA queue. * * MidiCodec: Auxiliary class to translate between raw MIDI streams and ALSA events. * * @see https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_event.html * @see https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_events.html * @see https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_ev_type.html * @see https://www.alsa-project.org/alsa-doc/alsa-lib/group___m_i_d_i___event.html * @} */ /** * Default constructor. */ SequencerEvent::SequencerEvent() : QEvent(SequencerEventType) { snd_seq_ev_clear( &m_event ); } /** * Constructor from an ALSA event record * @param event ALSA event record */ SequencerEvent::SequencerEvent(const snd_seq_event_t* event) : QEvent(SequencerEventType) { snd_seq_ev_clear( &m_event ); m_event = *event; } /** * Copy constructor * @param other A SequencerEvent object reference */ SequencerEvent::SequencerEvent(const SequencerEvent& other) : QEvent(SequencerEventType) { snd_seq_ev_clear( &m_event ); m_event = other.m_event; } /** * Assignment operator * @param other A SequencerEvent object reference * @return This object */ SequencerEvent& SequencerEvent::operator=(const SequencerEvent& other) { m_event = other.m_event; return *this; } /** * Checks if the event's type is a subscription. * @param event A SequencerEvent object pointer * @return True if the event has a subscribe/unsubscribe type. */ bool SequencerEvent::isSubscription(const SequencerEvent* event) { snd_seq_event_type_t te = event->getSequencerType(); return ( te == SND_SEQ_EVENT_PORT_SUBSCRIBED || te == SND_SEQ_EVENT_PORT_UNSUBSCRIBED ); } /** * Checks if the event's type is of type port. * @param event A SequencerEvent object pointer * @return True if the event has a port start/exit/change type. */ bool SequencerEvent::isPort(const SequencerEvent* event) { snd_seq_event_type_t te = event->getSequencerType(); return ( te == SND_SEQ_EVENT_PORT_START || te == SND_SEQ_EVENT_PORT_EXIT || te == SND_SEQ_EVENT_PORT_CHANGE ); } /** * Checks if the event's type is of type client. * @param event A SequencerEvent object pointer * @return True if the event has a client start/exit/change type. */ bool SequencerEvent::isClient(const SequencerEvent* event) { snd_seq_event_type_t te = event->getSequencerType(); return ( te == SND_SEQ_EVENT_CLIENT_START || te == SND_SEQ_EVENT_CLIENT_EXIT || te == SND_SEQ_EVENT_CLIENT_CHANGE ); } /** * Checks if the event's type is of type connection change. * @param event A SequencerEvent object pointer * @return True if the event has a client/port/subscription type. */ bool SequencerEvent::isConnectionChange(const SequencerEvent* event) { snd_seq_event_type_t te = event->getSequencerType(); return ( te == SND_SEQ_EVENT_PORT_START || te == SND_SEQ_EVENT_PORT_EXIT || te == SND_SEQ_EVENT_PORT_CHANGE || te == SND_SEQ_EVENT_CLIENT_START || te == SND_SEQ_EVENT_CLIENT_EXIT || te == SND_SEQ_EVENT_CLIENT_CHANGE || te == SND_SEQ_EVENT_PORT_SUBSCRIBED || te == SND_SEQ_EVENT_PORT_UNSUBSCRIBED ); } /** * Checks if the event's type is a Channel Voice message. * @param event A SequencerEvent object pointer * @return True if the event is a channel voice message. * @since 0.2.0 */ bool SequencerEvent::isChannel(const SequencerEvent* event) { snd_seq_event_type_t te = event->getSequencerType(); return ( te == SND_SEQ_EVENT_NOTEOFF || te == SND_SEQ_EVENT_NOTEON || te == SND_SEQ_EVENT_NOTE || te == SND_SEQ_EVENT_KEYPRESS || te == SND_SEQ_EVENT_CONTROLLER || te == SND_SEQ_EVENT_CONTROL14 || te == SND_SEQ_EVENT_PGMCHANGE || te == SND_SEQ_EVENT_CHANPRESS || te == SND_SEQ_EVENT_PITCHBEND ); } /** * Sets the event's ALSA sequencer type * @param eventType The ALSA sequencer type */ void SequencerEvent::setSequencerType(const snd_seq_event_type_t eventType) { m_event.type = eventType; } /** * Sets the client:port destination of the event. * @param client The destination's client ID * @param port The destination port ID * @see setSubscribers() */ void SequencerEvent::setDestination(const unsigned char client, const unsigned char port) { snd_seq_ev_set_dest(&m_event, client, port); } /** * Sets the event's source port ID * @param port The source port ID * @see getSourceClient(), getSourcePort() */ void SequencerEvent::setSource(const unsigned char port) { snd_seq_ev_set_source(&m_event, port); } /** * Sets the event's destination to be all the subscribers of the source port. */ void SequencerEvent::setSubscribers() { snd_seq_ev_set_subs(&m_event); } /** * Sets the event's destination to be all queues/clients/ports/channels. */ void SequencerEvent::setBroadcast() { snd_seq_ev_set_broadcast(&m_event); } /** * Sets the event to be immediately delivered, not queued/scheduled. * @see scheduleTick(), scheduleReal() */ void SequencerEvent::setDirect() { snd_seq_ev_set_direct(&m_event); } /** * Sets the event to be scheduled in musical time (ticks) units. * @param queue The queue number to be used. * @param tick The time in ticks. * @param relative Use relative (to the current) time instead of absolute time. */ void SequencerEvent::scheduleTick(int queue, int tick, bool relative) { snd_seq_ev_schedule_tick(&m_event, queue, relative, tick); } /** * Sets the event to be scheduled in real (clock) time units. * @param queue The queue number to be used. * @param secs The time in whole seconds. * @param nanos The nanoseconds to be added. * @param relative Use relative (to the current) time instead of absolute time. */ void SequencerEvent::scheduleReal(int queue, ulong secs, ulong nanos, bool relative) { snd_seq_real_time_t rtime; rtime.tv_sec = secs; rtime.tv_nsec = nanos; snd_seq_ev_schedule_real(&m_event, queue, relative, &rtime); } /** * Sets the priority of the event. This is used in case of several events share * the same scheduling time. * * @param high Mark the event as a high priority one. */ void SequencerEvent::setPriority(const bool high) { snd_seq_ev_set_priority(&m_event, high); } /** * Sets the event's tag. This attribute is any arbitrary number, not used by * the ALSA library. Range limited to 0 thru 255. * @param aTag A tag number. */ void SequencerEvent::setTag(const unsigned char aTag) { #if SND_LIB_VERSION > 0x010008 snd_seq_ev_set_tag(&m_event, aTag); #else m_event.tag = aTag; #endif } /** * Gets an event's raw 32 bits parameter. * @param n The parameter index, between 0 and 2. * @return The parameter's value. * @see setRaw32() */ unsigned int SequencerEvent::getRaw32(const unsigned int n) const { if (n < 3) return m_event.data.raw32.d[n]; return 0; } /** * Sets an event's raw 32 bits parameter. * @param n The parameter index, between 0 and 2. * @param value The parameter's value. */ void SequencerEvent::setRaw32(const unsigned int n, const unsigned int value) { if (n < 3) m_event.data.raw32.d[n] = value; } /** * Gets an event's raw 8 bits parameter. * @param n The parameter index, between 0 and 11. * @return The parameter's value. * @see setRaw8() */ unsigned char SequencerEvent::getRaw8(const unsigned int n) const { if (n < 12) return m_event.data.raw8.d[n]; return 0; } /** * Sets an event's raw 8 bits parameter. * @param n The parameter index, between 0 and 11. * @param value The parameter's value. */ void SequencerEvent::setRaw8(const unsigned int n, const unsigned char value) { if (n < 12) m_event.data.raw8.d[n] = value; } /** * Releases the event record. * @deprecated the event record is not allocated, so you don't have to call this function */ void SequencerEvent::free() { snd_seq_free_event(&m_event); } /** * Gets the encoded length of the event record. * @return The encoded length. */ int SequencerEvent::getEncodedLength() { return snd_seq_event_length(&m_event); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ SequencerEvent* SequencerEvent::clone() const { return new SequencerEvent(&m_event); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ ChannelEvent* ChannelEvent::clone() const { return new ChannelEvent(&m_event); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ KeyEvent* KeyEvent::clone() const { return new KeyEvent(&m_event); } /** * Constructor using proper attribute values. * @param ch MIDI Channel. * @param key MIDI note. * @param vel Note velocity. * @param dur Note duration. */ NoteEvent::NoteEvent(const int ch, const int key, const int vel, const int dur) : KeyEvent() { snd_seq_ev_set_note(&m_event, ch, key, vel, dur); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ NoteEvent* NoteEvent::clone() const { return new NoteEvent(&m_event); } /** * Constructor using proper attribute values. * @param ch MIDI Channel. * @param key MIDI note. * @param vel Note velocity. */ NoteOnEvent::NoteOnEvent(int ch, int key, int vel) : KeyEvent() { snd_seq_ev_set_noteon(&m_event, ch, key, vel); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ NoteOnEvent* NoteOnEvent::clone() const { return new NoteOnEvent(&m_event); } /** * Constructor using proper attribute values. * @param ch MIDI Channel. * @param key MIDI note. * @param vel Note velocity. */ NoteOffEvent::NoteOffEvent(int ch, int key, int vel) : KeyEvent() { snd_seq_ev_set_noteoff(&m_event, ch, key, vel); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ NoteOffEvent* NoteOffEvent::clone() const { return new NoteOffEvent(&m_event); } /** * Constructor using proper attribute values. * @param ch MIDI Channel. * @param key MIDI note. * @param vel Note velocity. */ KeyPressEvent::KeyPressEvent(int ch, int key, int vel) : KeyEvent() { snd_seq_ev_set_keypress(&m_event, ch, key, vel); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ KeyPressEvent* KeyPressEvent::clone() const { return new KeyPressEvent(&m_event); } /** * Constructor using proper attribute values. * @param ch MIDI Channel. * @param cc MIDI Controller number. * @param val Controller value. */ ControllerEvent::ControllerEvent(int ch, int cc, int val) : ChannelEvent() { snd_seq_ev_set_controller(&m_event, ch, cc, val); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ ControllerEvent* ControllerEvent::clone() const { return new ControllerEvent(&m_event); } /** * Constructor using proper attribute values. * @param ch MIDI Channel. * @param val MIDI Program number. */ ProgramChangeEvent::ProgramChangeEvent(int ch, int val) : ChannelEvent() { snd_seq_ev_set_pgmchange(&m_event, ch, val); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ ProgramChangeEvent* ProgramChangeEvent::clone() const { return new ProgramChangeEvent(&m_event); } /** * Constructor using proper attribute values. * @param ch MIDI Channel. * @param val Pitch Bend value. Zero centered from -8192 to 8191. */ PitchBendEvent::PitchBendEvent(int ch, int val) : ChannelEvent() { snd_seq_ev_set_pitchbend(&m_event, ch, val); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ PitchBendEvent* PitchBendEvent::clone() const { return new PitchBendEvent(&m_event); } /** * Constructor using proper attribute values. * @param ch MIDI Channel. * @param val Aftertouch value. */ ChanPressEvent::ChanPressEvent(int ch, int val) : ChannelEvent() { snd_seq_ev_set_chanpress(&m_event, ch, val); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ ChanPressEvent* ChanPressEvent::clone() const { return new ChanPressEvent(&m_event); } /** * Default constructor. */ VariableEvent::VariableEvent() : SequencerEvent() { m_data.clear(); snd_seq_ev_set_variable ( &m_event, m_data.size(), m_data.data() ); } /** * Constructor from an ALSA event record. * @param event ALSA event record. */ VariableEvent::VariableEvent(const snd_seq_event_t* event) : SequencerEvent(event) { m_data = QByteArray((char *) event->data.ext.ptr, event->data.ext.len); snd_seq_ev_set_variable ( &m_event, m_data.size(), m_data.data() ); } /** * Constructor from an arbitrary data array. * @param data A data byte array. */ VariableEvent::VariableEvent(const QByteArray& data) : SequencerEvent() { m_data = data; snd_seq_ev_set_variable ( &m_event, m_data.size(), m_data.data() ); } /** * Copy constructor. * @param other Another VariableEvent instance. s */ VariableEvent::VariableEvent(const VariableEvent& other) : SequencerEvent(other) { m_data = other.m_data; snd_seq_ev_set_variable ( &m_event, m_data.size(), m_data.data() ); } /** * Constructor from a data pointer. * @param datalen Length of the data. * @param dataptr Pointer the data. */ VariableEvent::VariableEvent(const unsigned int datalen, char* dataptr) : SequencerEvent() { m_data = QByteArray(dataptr, datalen); snd_seq_ev_set_variable( &m_event, m_data.size(), m_data.data() ); } /** * Assignment operator. * @param other Another VariableEvent object reference * @return Pointer to this object */ VariableEvent& VariableEvent::operator=(const VariableEvent& other) { m_event = other.m_event; m_data = other.m_data; snd_seq_ev_set_variable ( &m_event, m_data.size(), m_data.data() ); return *this; } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ VariableEvent* VariableEvent::clone() const { return new VariableEvent(&m_event); } /** * Default constructor. */ SysExEvent::SysExEvent() : VariableEvent() { snd_seq_ev_set_sysex( &m_event, m_data.size(), m_data.data() ); } /** * Constructor from an ALSA event record. * @param event ALSA event record. */ SysExEvent::SysExEvent(const snd_seq_event_t* event) : VariableEvent(event) { snd_seq_ev_set_sysex( &m_event, m_data.size(), m_data.data() ); } /** * Constructor from a data array. * @param data A data byte array. */ SysExEvent::SysExEvent(const QByteArray& data) : VariableEvent(data) { snd_seq_ev_set_sysex( &m_event, m_data.size(), m_data.data() ); } /** * Copy constructor. * @param other Another SysExEvent object reference. */ SysExEvent::SysExEvent(const SysExEvent& other) : VariableEvent(other) { snd_seq_ev_set_sysex( &m_event, m_data.size(), m_data.data() ); } /** * Constructor taking a data pointer and length * @param datalen Data length * @param dataptr Data pointer */ SysExEvent::SysExEvent(const unsigned int datalen, char* dataptr) : VariableEvent( datalen, dataptr ) { snd_seq_ev_set_sysex( &m_event, m_data.size(), m_data.data() ); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ SysExEvent* SysExEvent::clone() const { return new SysExEvent(&m_event); } /** * Assignment operator. * @param other Another SysExEvent object reference * @return pointer to this object */ SysExEvent &SysExEvent::operator=(const SysExEvent &other) { m_event = other.m_event; m_data = other.m_data; snd_seq_ev_set_sysex(&m_event, m_data.size(), m_data.data()); return *this; } /** * Default constructor */ TextEvent::TextEvent() : VariableEvent(), m_textType(1) { setSequencerType(SND_SEQ_EVENT_USR_VAR0); } /** * Constructor from an ALSA sequencer record. * @param event ALSA sequencer record. */ TextEvent::TextEvent(const snd_seq_event_t* event) : VariableEvent(event), m_textType(1) { setSequencerType(SND_SEQ_EVENT_USR_VAR0); } /** * Constructor from a given string * @param text The event's text * @param textType The SMF text type */ TextEvent::TextEvent(const QString& text, const int textType) : VariableEvent(text.toUtf8()), m_textType(textType) { setSequencerType(SND_SEQ_EVENT_USR_VAR0); } /** * Copy constructor * @param other An existing TextEvent object reference */ TextEvent::TextEvent(const TextEvent& other) : VariableEvent(other) { setSequencerType(SND_SEQ_EVENT_USR_VAR0); m_textType = other.getTextType(); } /** * Constructor from a data pointer and length * @param datalen Data length * @param dataptr Data pointer */ TextEvent::TextEvent(const unsigned int datalen, char* dataptr) : VariableEvent(datalen, dataptr), m_textType(1) { setSequencerType(SND_SEQ_EVENT_USR_VAR0); } /** * Gets the event's text content. * @return The text content. */ QString TextEvent::getText() const { return QString::fromUtf8(m_data.data(), m_data.size()); } /** * Gets the event's SMF text type. * @return The SMF text type. */ int TextEvent::getTextType() const { return m_textType; } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ TextEvent* TextEvent::clone() const { return new TextEvent(&m_event); } /** * Assignment operator. * @param other Another TextEvent object reference * @return pointer to this object */ TextEvent &TextEvent::operator=(const TextEvent &other) { m_event = other.m_event; m_data = other.m_data; m_textType = other.getTextType(); snd_seq_ev_set_variable(&m_event, m_data.size(), m_data.data()); setSequencerType(SND_SEQ_EVENT_USR_VAR0); return *this; } /** * Constructor * @param type The event's type */ SystemEvent::SystemEvent(const snd_seq_event_type_t type) : SequencerEvent() { snd_seq_ev_set_fixed(&m_event); setSequencerType(type); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ SystemEvent* SystemEvent::clone() const { return new SystemEvent(&m_event); } /** * Constructor * @param type Event type * @param queue Queue number * @param value Value */ QueueControlEvent::QueueControlEvent(snd_seq_event_type_t type, int queue, int value) : SequencerEvent() { snd_seq_ev_set_queue_control(&m_event, type, queue, value); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ QueueControlEvent* QueueControlEvent::clone() const { return new QueueControlEvent(&m_event); } /** * Constructor * @param type The event's type * @param val Value */ ValueEvent::ValueEvent(const snd_seq_event_type_t type, int val) : SequencerEvent() { snd_seq_ev_set_fixed(&m_event); setSequencerType(type); setValue(val); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ ValueEvent* ValueEvent::clone() const { return new ValueEvent(&m_event); } /** * Constructor * @param queue Queue number. * @param tempo Tempo value in microseconds per quarter note. */ TempoEvent::TempoEvent(int queue, int tempo) : QueueControlEvent() { snd_seq_ev_set_queue_tempo(&m_event, queue, tempo); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ TempoEvent* TempoEvent::clone() const { return new TempoEvent(&m_event); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ ClientEvent* ClientEvent::clone() const { return new ClientEvent(&m_event); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ PortEvent* PortEvent::clone() const { return new PortEvent(&m_event); } /** * Clone this object returning a pointer to the new object * @return pointer to the new object */ SubscriptionEvent* SubscriptionEvent::clone() const { return new SubscriptionEvent(&m_event); } /** * Default constructor. */ RemoveEvents::RemoveEvents() { snd_seq_remove_events_malloc(&m_Info); } /** * Copy constructor. * @param other An existing RemoveEvents object reference. */ RemoveEvents::RemoveEvents(const RemoveEvents& other) { snd_seq_remove_events_malloc(&m_Info); snd_seq_remove_events_copy(m_Info, other.m_Info); } /** * Constructor from an ALSA remove events object pointer. * @param other An ALSA remove events object pointer. */ RemoveEvents::RemoveEvents(snd_seq_remove_events_t* other) { snd_seq_remove_events_malloc(&m_Info); snd_seq_remove_events_copy(m_Info, other); } /** * Destructor. */ RemoveEvents::~RemoveEvents() { snd_seq_remove_events_free(m_Info); } /** * Create a new object copied from this object and return a pointer to the copy. * @return A pointer to the new object. */ RemoveEvents* RemoveEvents::clone() { return new RemoveEvents(m_Info); } /** * Assignment operator. * @param other An existing RemoveEvents object reference. * @return This object. */ RemoveEvents& RemoveEvents::operator=(const RemoveEvents& other) { if (this == &other) return *this; snd_seq_remove_events_copy(m_Info, other.m_Info); return *this; } /** * Gets the allocated size of the ALSA remove events object. * @return The size of the ALSA remove events object. */ int RemoveEvents::getSizeOfInfo() const { return snd_seq_remove_events_sizeof(); } /** * Gets the MIDI channel. * @return The MIDI channel. * @see setChannel() */ int RemoveEvents::getChannel() { return snd_seq_remove_events_get_channel(m_Info); } /** * Gets the condition. * @return The condition. * @see setCondition() */ unsigned int RemoveEvents::getCondition() { return snd_seq_remove_events_get_condition(m_Info); } /** * Gets the destination. * @return The destination record pointer. * @see setDest() */ const snd_seq_addr_t* RemoveEvents::getDest() { return snd_seq_remove_events_get_dest(m_Info); } /** * Gets the event type. * @return The event type. * @see setEventType() */ int RemoveEvents::getEventType() { return snd_seq_remove_events_get_event_type(m_Info); } /** * Gets the queue number. * @return The queue number. * @see setQueue() */ int RemoveEvents::getQueue() { return snd_seq_remove_events_get_queue(m_Info); } /** * Gets the numeric tag. * @return The numeric tag. * @see setTag() */ int RemoveEvents::getTag() { return snd_seq_remove_events_get_tag(m_Info); } /** * Gets the timestamp. * @return The timestamp. * @see setTime() */ const snd_seq_timestamp_t* RemoveEvents::getTime() { return snd_seq_remove_events_get_time(m_Info); } /** * Gets the MIDI channel. * @param chan The MIDI channel. * @see getChannel() */ void RemoveEvents::setChannel(int chan) { snd_seq_remove_events_set_channel(m_Info, chan); } /** * Sets the flags of the conditional event's removal. This condition is a * bitmap of the combination (OR) the following auto-described flags: *
    *
  • SND_SEQ_REMOVE_INPUT
  • *
  • SND_SEQ_REMOVE_OUTPUT
  • *
  • SND_SEQ_REMOVE_DEST
  • *
  • SND_SEQ_REMOVE_DEST_CHANNEL
  • *
  • SND_SEQ_REMOVE_TIME_BEFORE
  • *
  • SND_SEQ_REMOVE_TIME_AFTER
  • *
  • SND_SEQ_REMOVE_TIME_TICK
  • *
  • SND_SEQ_REMOVE_EVENT_TYPE
  • *
  • SND_SEQ_REMOVE_IGNORE_OFF
  • *
  • SND_SEQ_REMOVE_TAG_MATCH
  • *
* @param cond The condition bitmap. * @see getCondition() */ void RemoveEvents::setCondition(unsigned int cond) { snd_seq_remove_events_set_condition(m_Info, cond); } /** * Set the destination address. * @param dest A pointer to the destination address record. * @see getDest() */ void RemoveEvents::setDest(const snd_seq_addr_t* dest) { snd_seq_remove_events_set_dest(m_Info, dest); } /** * Sets the event type. * @param type The event type. * @see getEventType() */ void RemoveEvents::setEventType(int type) { snd_seq_remove_events_set_event_type(m_Info, type); } /** * Sets the queue number. * @param queue The queue number. * @see getQueue() */ void RemoveEvents::setQueue(int queue) { snd_seq_remove_events_set_queue(m_Info, queue); } /** * Sets the numeric tag. * @param tag The numeric tag. * @see getTag() */ void RemoveEvents::setTag(int tag) { snd_seq_remove_events_set_tag(m_Info, tag); } /** * Sets the timestamp. * @param time A pointer to the timestamp record. * @see getTime() */ void RemoveEvents::setTime(const snd_seq_timestamp_t* time) { snd_seq_remove_events_set_time(m_Info, time); } /** * MidiCodec constructor * @param bufsize The buffer size of the CODEC * @param parent The optional parent object */ MidiCodec::MidiCodec( int bufsize, QObject* parent ) : QObject(parent) { DRUMSTICK_ALSA_CHECK_ERROR(snd_midi_event_new(bufsize, &m_Info)); } /** * Destructor */ MidiCodec::~MidiCodec() { snd_midi_event_free(m_Info); } /** * CODEC initialization. */ void MidiCodec::init() { snd_midi_event_init(m_Info); } /** * Decode from event to bytes. * @param buf A buffer to get the results * @param count Available bytes in MIDI byte stream * @param ev The input event * @return The number of written bytes if success. */ long MidiCodec::decode(unsigned char *buf, long count, const snd_seq_event_t *ev) { return DRUMSTICK_ALSA_CHECK_WARNING(snd_midi_event_decode(m_Info, buf, count, ev)); } /** * Encode from byte stream. * @param buf MIDI byte stream * @param count Bytes of MIDI byte stream to encode * @param ev Result - sequencer event * @return Number of written bytes if success. */ long MidiCodec::encode(const unsigned char *buf, long count, snd_seq_event_t *ev) { return DRUMSTICK_ALSA_CHECK_WARNING(snd_midi_event_encode(m_Info, buf, count, ev)); } /** * Read one byte and encode to sequencer event if finished. * @param c A byte of MIDI stream * @param ev Result - sequencer event * @return 1 - sequencer event is completed, 0 - next byte is required for completion, otherwise a negative error code */ long MidiCodec::encode(int c, snd_seq_event_t *ev) { return DRUMSTICK_ALSA_CHECK_WARNING(snd_midi_event_encode_byte(m_Info, c, ev)); } /** * Enable MIDI running status (command merge) * @param enable True to enable, false to disable. */ void MidiCodec::enableRunningStatus(bool enable) { snd_midi_event_no_status(m_Info, enable ? 0 : 1); } /** * Reset MIDI decode parser. */ void MidiCodec::resetDecoder() { snd_midi_event_reset_decode(m_Info); } /** * Reset MIDI encode parser. */ void MidiCodec::resetEncoder() { snd_midi_event_reset_encode(m_Info); } /** * Resize the CODEC buffer * @param bufsize New buffer size. */ void MidiCodec::resizeBuffer(int bufsize) { DRUMSTICK_ALSA_CHECK_WARNING(snd_midi_event_resize_buffer(m_Info, bufsize)); } } // namespace ALSA } // namespace drumstick drumstick-2.9.0/library/alsa/CMakeLists.txt0000644000175000017500000000771214541630232017666 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-alsa_QTOBJ_SRCS ../include/drumstick/alsaclient.h ../include/drumstick/alsaevent.h ../include/drumstick/alsaport.h ../include/drumstick/alsaqueue.h ../include/drumstick/alsatimer.h ../include/drumstick/playthread.h ) set(drumstick-alsa_HEADERS ../include/drumstick/alsaclient.h ../include/drumstick/alsaevent.h ../include/drumstick/alsaport.h ../include/drumstick/alsaqueue.h ../include/drumstick/alsatimer.h ../include/drumstick/playthread.h ../include/drumstick/sequencererror.h ../include/drumstick/subscription.h ../include/drumstick/macros.h ) set(drumstick-alsa_SRCS alsaclient.cpp alsaevent.cpp alsaport.cpp alsaqueue.cpp alsatimer.cpp playthread.cpp sequencererror.cpp subscription.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-alsa_MOC_SRCS ${drumstick-alsa_QTOBJ_SRCS}) else() qt_wrap_cpp(drumstick-alsa_MOC_SRCS ${drumstick-alsa_QTOBJ_SRCS}) endif() add_library(drumstick-alsa ${drumstick-alsa_MOC_SRCS} ${drumstick-alsa_SRCS} ${drumstick-alsa_HEADERS} ) add_library(Drumstick::ALSA ALIAS drumstick-alsa) target_include_directories(drumstick-alsa PUBLIC $ $ ) #message(STATUS "ALSA -- HAVE_DBUS: ${HAVE_DBUS}") target_compile_definitions(drumstick-alsa PRIVATE QT_NO_SIGNALS_SLOTS_KEYWORDS $<$:RTKIT_SUPPORT> ) target_link_libraries(drumstick-alsa PRIVATE Qt${QT_VERSION_MAJOR}::Core PkgConfig::ALSA ) if(USE_DBUS) target_link_libraries(drumstick-alsa PRIVATE Qt${QT_VERSION_MAJOR}::DBus ) endif() if(STATIC_DRUMSTICK) set_target_properties(drumstick-alsa PROPERTIES STATIC_LIB "libdrumstick-alsa" EXPORT_NAME ALSA ) else() set_target_properties(drumstick-alsa PROPERTIES VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH} SOVERSION ${PROJECT_VERSION_MAJOR} EXPORT_NAME ALSA ) endif() install(TARGETS drumstick-alsa EXPORT drumstick-alsa-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) install(FILES ${drumstick-alsa_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/drumstick) install(EXPORT drumstick-alsa-targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/drumstick NAMESPACE Drumstick:: ) export(EXPORT drumstick-alsa-targets NAMESPACE Drumstick:: FILE ${CMAKE_BINARY_DIR}/drumstick-alsa-targets.cmake ) include(CMakePackageConfigHelpers) write_basic_package_version_file( ${CMAKE_BINARY_DIR}/drumstick-alsa-config-version.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY AnyNewerVersion ) configure_file( drumstick-alsa-config.cmake ${CMAKE_BINARY_DIR}/drumstick-alsa-config.cmake @ONLY ) install(FILES ${CMAKE_BINARY_DIR}/drumstick-alsa-config-version.cmake ${CMAKE_BINARY_DIR}/drumstick-alsa-config.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/drumstick ) drumstick-2.9.0/library/alsa/alsaclient.cpp0000644000175000017500000020205514541630232017746 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "errorcheck.h" #include #include #include #include #include #include #include #include #include #if defined(RTKIT_SUPPORT) #include #include #include #include #include #endif #include #ifndef RLIMIT_RTTIME #define RLIMIT_RTTIME 15 #endif #ifndef SCHED_RESET_ON_FORK #define SCHED_RESET_ON_FORK 0x40000000 #endif #ifndef DEFAULT_INPUT_TIMEOUT #define DEFAULT_INPUT_TIMEOUT 500 #endif /** * @file alsaclient.cpp * Implementation of classes managing ALSA Sequencer clients */ /** * @class QObject * The QObject class is the base class of all Qt objects. * @see https://doc.qt.io/qt-5/qobject.html */ /** * @class QThread * The QThread class provides platform-independent threads. * @see https://doc.qt.io/qt-5/qthread.html */ namespace drumstick { namespace ALSA { /** * @addtogroup ALSAClient * @{ * * ALSA clients are any entities using ALSA sequencer services. A client * may be an application or a device driver for an external MIDI port, like * USB MIDI devices or the MIDI/game ports of some sound cards. This library * allows to easily create applications managing ALSA clients. * * ALSA clients are also file descriptors representing a sequencer device, * that must be opened before reading or writing MIDI events. When the client * is opened, it is given some handle and a number identifying it to other * clients in the system. You can also provide a name for it. * * Each ALSA sequencer client can have several ports attached. The ports can be * readable or writable, and can be subscribed in pairs: one readable port to * one writable port. The subscriptions can be made and queried by external * applications, like "aconnect" or "qjackctl". * * SystemInfo is an auxiliary class to query several system capabilities. * * The PoolInfo class represents a container to query and change some values * for the kernel memory pool assigned to an ALSA client. * * The ClientInfo class is another container to query and change properties of * the MidiClient itself. * * The SequencerEventHandler abstract class is used to define an interface * that other class can implement to receive sequencer events. It is one of the * three methods of delivering input events offered by the library. * * @section EventInput Input * MidiClient uses a separate thread to receive events from the ALSA sequencer. * The input thread can be started and stopped using the methods * MidiClient::startSequencerInput() and MidiClient::stopSequencerInput(). * It is necessary to have this thread in mind when using this library to read * events. There are three delivering methods of input events: *
    *
  • A Callback method. To use this method, you must derive a class from * SequencerEventHandler, overriding the method * SequencerEventHandler::handleSequencerEvent() to provide your own event * processing code. You must give a handler instance pointer to * the client using MidiClient::setHandler().
  • *
  • Using QEvent listeners. To use this method, you must have one or more * classes derived from QObject overriding the method QObject::customEvent(). * You must also use the method MidiClient::addListener() to add such objects * to the client's listeners list, and MidiClient::setEventsEnabled().
  • *
  • The third method involves signals and slots. Whenever a sequencer event * is received, a signal MidiClient::eventReceived() is emitted, that can be * connected to your own supplied slot(s) to process it. *
* The selected method depends only on your requirements and your preferences. *
    *
  • The Callback method is preferred for real-time usage because the handler * receives the events without any delay, but at the same time you must * avoid calling methods of any GUI widgets within the handler. Instead, * you can create QEvents and call QObject::postEvent() to notify the GUI.
  • *
  • Inside QObject::eventReceiver() you can collect QEvents and call * any method you want, but the events are not delivered in real-time. Instead, * they are enqueued and dispatched by the main application's event loop.
  • *
  • The signals/slots method can be real-time or queued, depending on the * last parameter of QObject::connect(). If it is Qt::DirectConnection, the signal * is delivered in real-time, and the same rule about avoiding calls to any * GUI widgets methods apply. If it is Qt::QueuedConnection, then the signal is * enqueued using the application's event loop, and it is safe to call any GUI * methods in this case.
  • *
* Whichever method you select, it excludes the other methods for the same * program. A callback takes precedence over the others. If it is not set, then * the events are sent if MidiClient::setEventsEnabled() is called. * If neither a callback handler is set nor events are enabled, then the signal * is emitted. In any case, the event pointer must be deleted by the receiver * method. * * @see https://doc.qt.io/qt-5/threads-reentrancy.html * * @section EventOutput Output * * The methods to send a single event to the ALSA sequencer are: *
    *
  • MidiClient::output() using the library buffer, automatically flushed.
  • *
  • MidiClient::outputBuffer() using the library buffer. Not flushed automatically.
  • *
  • MidiClient::outputDirect() not using the library buffer.
  • *
* The two first methods usually require a call to MidiClient::drainOutput() to * flush the ALSA library output buffer. The third one bypasses the buffer, and * doesn't require the call to MidiClient::drainOutput(). Note that the buffer * can be automatically drained by the first method when it becomes full. * * After being dispatched to the ALSA Sequencer, the events can be scheduled at * some time in the future, or immediately. This depends on the following * methods of the SequencerEvent class: *
    *
  • SequencerEvent::setDirect() not scheduled
  • *
  • SequencerEvent::scheduleTick() scheduled in musical time (ticks)
  • *
  • SequencerEvent::scheduleReal() scheduled in clock time (seconds)
  • *
* * When you need to schedule a lot of events, for instance reproducing * a Standard MIDI File (SMF) or a MIDI sequence, you may want to use the * abstract class SequencerOutputThread. * * @section Memory * * There are two memory issues: the memory pool belongs to the kernel sequencer, * and can be managed by the class PoolInfo and the methods * MidiClient::getPoolInfo() and MidiClient::setPoolInfo(). The library buffer * can be controlled using the methods MidiClient::getOutputBufferSize() and * MidiClient::setOutputBufferSize() as well as MidiClient::getInputBufferSize() * and MidiClient::setInputBufferSize(). * * @see https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_client.html */ /** * This class manages event input from the ALSA sequencer. */ class MidiClient::SequencerInputThread: public QThread { public: SequencerInputThread(MidiClient *seq, int timeout) : QThread(), m_MidiClient(seq), m_Wait(timeout), m_Stopped(false), m_RealTime(true) {} virtual ~SequencerInputThread() = default; void run() override; bool stopped(); void stop(); void setRealtimePriority(); MidiClient *m_MidiClient; int m_Wait; bool m_Stopped; bool m_RealTime; QReadWriteLock m_mutex; }; class MidiClient::MidiClientPrivate { public: MidiClientPrivate() : m_eventsEnabled(false), m_BlockMode(false), m_NeedRefreshClientList(true), m_OpenMode(SND_SEQ_OPEN_DUPLEX), m_DeviceName("default"), m_SeqHandle(nullptr), m_Thread(nullptr), m_Queue(nullptr), m_handler(nullptr) { } bool m_eventsEnabled; bool m_BlockMode; bool m_NeedRefreshClientList; int m_OpenMode; QString m_DeviceName; snd_seq_t* m_SeqHandle; QPointer m_Thread; QPointer m_Queue; SequencerEventHandler* m_handler; ClientInfo m_Info; ClientInfoList m_ClientList; MidiPortList m_Ports; PortInfoList m_OutputsAvail; PortInfoList m_InputsAvail; QObjectList m_listeners; SystemInfo m_sysInfo; PoolInfo m_poolInfo; }; /** * Constructor. * * This constructor optionally gets a QObject parent. When you create a * MidiClient with another object as parent, the MidiClient object will * automatically add itself to the parent's children() list. The parent takes * ownership of the object i.e. it will automatically delete its children in * its destructor. * * It is necessary to invoke open() later to get the sequencer client handler * from the ALSA sequencer subsystem. * * @param parent The optional parent object */ MidiClient::MidiClient( QObject* parent ) : QObject(parent), d(new MidiClientPrivate) { qRegisterMetaType(); qRegisterMetaType(); } /** * Destructor. * * The ports and queue associated to this client are automatically released. */ MidiClient::~MidiClient() { stopSequencerInput(); detachAllPorts(); delete d->m_Queue; close(); freeClients(); delete d->m_Thread; } /** * Returns the sequencer handler managed by ALSA * @return the sequencer handler */ snd_seq_t* MidiClient::getHandle() { return d->m_SeqHandle; } /** * Returns true if the sequencer is opened * @return wheter the sequencer is opened */ bool MidiClient::isOpened() { return !d.isNull() && (d->m_SeqHandle != nullptr); } /** * Returns the name of the sequencer device * @return the device name */ QString MidiClient::getDeviceName() { return d->m_DeviceName; } /** * Returns the last open mode used in open() * @return the last open mode */ int MidiClient::getOpenMode() { return d->m_OpenMode; } /** * Returns the last block mode used in open() * @return the last block mode */ bool MidiClient::getBlockMode() { return d->m_BlockMode; } /** * Returns true if the events mode of delivery has been enabled * @return whether the events mode of delivery is enabled */ bool MidiClient::getEventsEnabled() const { return d->m_eventsEnabled; } /** * Sets a sequencer event handler enabling the callback delivery mode * @param handler the sequencer event handler */ void MidiClient::setHandler(SequencerEventHandler* handler) { d->m_handler = handler; } /** * Enables real-time priority for the MIDI input thread. The system needs either * RLIMIT_RTPRIO or RealtimeKit. First RLIMIT_RTPRIO is tried, and if this * method fails, RealtimeKit is used. * * @param enable real-time priority enabled * @since 0.5.0 */ void MidiClient::setRealTimeInput(bool enable) { if (d->m_Thread == nullptr) { d->m_Thread = new SequencerInputThread(this, DEFAULT_INPUT_TIMEOUT); d->m_Thread->m_RealTime = enable; } } /** * Return the real-time priority setting for the MIDI input thread. * @return true if the real-time priority is enabled * @since 0.5.0 */ bool MidiClient::realTimeInputEnabled() { if (d->m_Thread == nullptr) return true; return d->m_Thread->m_RealTime; } /** * Open the sequencer device. * * When opening the MidiClient instance, several properties may optionally * be set as the device name, the open mode and block mode. Default values * are provided for them. After a successful open, an event with * SND_SEQ_EVENT_CLIENT_START is broadcast to the announce port. * * @param deviceName the sequencer device name, default value = "default". * This is not a name you make up for your own purposes; it has special * significance to the ALSA library. Usually you need to pass "default" here. * @param openMode the open mode, default value = SND_SEQ_OPEN_DUPLEX. * The read/write mode of the sequencer. Can be one of these three values: *
    *
  • SND_SEQ_OPEN_OUTPUT - open the sequencer for output only
  • *
  • SND_SEQ_OPEN_INPUT - open the sequencer for input only
  • *
  • SND_SEQ_OPEN_DUPLEX - open the sequencer for output and input
  • *
* @param blockMode open in blocking mode, default value = false. */ void MidiClient::open( const QString deviceName, const int openMode, const bool blockMode) { DRUMSTICK_ALSA_CHECK_ERROR( snd_seq_open( &d->m_SeqHandle, deviceName.toLocal8Bit().data(), openMode, blockMode ? 0 : SND_SEQ_NONBLOCK ) ); DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_get_client_info( d->m_SeqHandle, d->m_Info.m_Info ) ); d->m_DeviceName = deviceName; d->m_OpenMode = openMode; d->m_BlockMode = blockMode; } /** * Open the sequencer device, providing a configuration object pointer. * * This method is like open() except that a configuration is explicitly * provided. After a successful open, an event with SND_SEQ_EVENT_CLIENT_START * type is broadcasted from the announce port. * * @param conf a configuration object pointer. * @param deviceName the sequencer device name, default value = "default". * This is not a name you make up for your own purposes; it has special * significance to the ALSA library. Usually you need to pass "default" here. * @param openMode the open mode, default value = SND_SEQ_OPEN_DUPLEX. * The read/write mode of the sequencer. Can be one of these three values: *
    *
  • SND_SEQ_OPEN_OUTPUT - open the sequencer for output only
  • *
  • SND_SEQ_OPEN_INPUT - open the sequencer for input only
  • *
  • SND_SEQ_OPEN_DUPLEX - open the sequencer for output and input
  • *
* @param blockMode open in blocking mode, default value = false. */ void MidiClient::open( snd_config_t* conf, const QString deviceName, const int openMode, const bool blockMode ) { DRUMSTICK_ALSA_CHECK_ERROR( snd_seq_open_lconf( &d->m_SeqHandle, deviceName.toLocal8Bit().data(), openMode, blockMode ? 0 : SND_SEQ_NONBLOCK, conf )); DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_get_client_info(d->m_SeqHandle, d->m_Info.m_Info)); d->m_DeviceName = deviceName; d->m_OpenMode = openMode; d->m_BlockMode = blockMode; } /** * Close the sequencer device. * * After a client is closed, an event with SND_SEQ_EVENT_CLIENT_EXIT is * broadcast to the announce port. The connection between other clients are * disconnected. Call this just before exiting your program. */ void MidiClient::close() { if (d->m_SeqHandle != nullptr) { stopSequencerInput(); DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_close(d->m_SeqHandle)); d->m_SeqHandle = nullptr; } } /** * Gets the size of the library output buffer for the ALSA client. * * This buffer is used to store the decoded byte-stream of output events before * transferring to the sequencer. * * @return the size of the library output buffer */ size_t MidiClient::getOutputBufferSize() { return snd_seq_get_output_buffer_size(d->m_SeqHandle); } /** * Sets the size of the library output buffer for the ALSA client. * * This buffer is used to store the decoded byte-stream of output events before * transferring to the sequencer. * * @param newSize the size of the library output buffer */ void MidiClient::setOutputBufferSize(size_t newSize) { if (getOutputBufferSize() != newSize) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_output_buffer_size(d->m_SeqHandle, newSize)); } } /** * Gets the size of the library input buffer for the ALSA client. * * This buffer is used to read a byte-stream of input events before * transferring from the sequencer. * * @return the size of the library input buffer */ size_t MidiClient::getInputBufferSize() { return snd_seq_get_input_buffer_size(d->m_SeqHandle); } /** * Sets the size of the library input buffer for the ALSA client. * * This buffer is used to read a byte-stream of input events before * transferring from the sequencer. * * @param newSize the size of the library input buffer */ void MidiClient::setInputBufferSize(size_t newSize) { if (getInputBufferSize() != newSize) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_input_buffer_size(d->m_SeqHandle, newSize)); } } /** * Change the blocking mode of the client. * * In block mode, the client falls into sleep when it fills the output memory * pool with full events. The client will be woken up after a certain amount * of free space becomes available. * * @param newValue the blocking mode */ void MidiClient::setBlockMode(bool newValue) { if (d->m_BlockMode != newValue) { d->m_BlockMode = newValue; if (d->m_SeqHandle != nullptr) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_nonblock(d->m_SeqHandle, d->m_BlockMode ? 0 : 1)); } } } /** * Gets the client ID. * * Returns the ID of the client. A client ID is necessary to inquiry or to set * the client information. A user client ID is assigned from 128 to 191. * * @return the client ID. */ int MidiClient::getClientId() { return DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_client_id(d->m_SeqHandle)); } /** * Returns the type snd_seq_type_t of the given sequencer handle. * @return the type snd_seq_type_t of the given sequencer handle. */ snd_seq_type_t MidiClient::getSequencerType() { return snd_seq_type(d->m_SeqHandle); } /** * Dispatch the events received from the Sequencer. * * There are three methods of events delivering: *
    *
  • A Callback method. To use this method, you must derive a class from * SequencerEventHandler, overriding the method * SequencerEventHandler::handleSequencerEvent() to * provide your own event processing. You must provide the handler instance to * the client using setHandler().
  • *
  • Using QEvent listeners. To use this method, you must use one or more * classes derived from QObject overriding the method QObject::customEvent(). * You must also use the method addListener() to add such objects to the * client's listeners list.
  • *
  • The third method involves signals and slots. Whenever a sequencer event * is received, a signal eventReceived() is emitted, that can be connected to * your own supplied slot(s) to process it. *
* @see ALSAClient */ void MidiClient::doEvents() { do { int err = 0; snd_seq_event_t* evp = nullptr; SequencerEvent* event = nullptr; err = snd_seq_event_input(d->m_SeqHandle, &evp); if ((err >= 0) && (evp != nullptr)) { switch (evp->type) { case SND_SEQ_EVENT_NOTE: event = new NoteEvent(evp); break; case SND_SEQ_EVENT_NOTEON: event = new NoteOnEvent(evp); break; case SND_SEQ_EVENT_NOTEOFF: event = new NoteOffEvent(evp); break; case SND_SEQ_EVENT_KEYPRESS: event = new KeyPressEvent(evp); break; case SND_SEQ_EVENT_CONTROLLER: case SND_SEQ_EVENT_CONTROL14: case SND_SEQ_EVENT_REGPARAM: case SND_SEQ_EVENT_NONREGPARAM: event = new ControllerEvent(evp); break; case SND_SEQ_EVENT_PGMCHANGE: event = new ProgramChangeEvent(evp); break; case SND_SEQ_EVENT_CHANPRESS: event = new ChanPressEvent(evp); break; case SND_SEQ_EVENT_PITCHBEND: event = new PitchBendEvent(evp); break; case SND_SEQ_EVENT_SYSEX: event = new SysExEvent(evp); break; case SND_SEQ_EVENT_PORT_SUBSCRIBED: case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: event = new SubscriptionEvent(evp); break; case SND_SEQ_EVENT_PORT_CHANGE: case SND_SEQ_EVENT_PORT_EXIT: case SND_SEQ_EVENT_PORT_START: event = new PortEvent(evp); d->m_NeedRefreshClientList = true; break; case SND_SEQ_EVENT_CLIENT_CHANGE: case SND_SEQ_EVENT_CLIENT_EXIT: case SND_SEQ_EVENT_CLIENT_START: event = new ClientEvent(evp); d->m_NeedRefreshClientList = true; break; case SND_SEQ_EVENT_SONGPOS: case SND_SEQ_EVENT_SONGSEL: case SND_SEQ_EVENT_QFRAME: case SND_SEQ_EVENT_TIMESIGN: case SND_SEQ_EVENT_KEYSIGN: event = new ValueEvent(evp); break; case SND_SEQ_EVENT_SETPOS_TICK: case SND_SEQ_EVENT_SETPOS_TIME: case SND_SEQ_EVENT_QUEUE_SKEW: event = new QueueControlEvent(evp); break; case SND_SEQ_EVENT_TEMPO: event = new TempoEvent(evp); break; default: event = new SequencerEvent(evp); break; } // first, process the callback (if any) if (d->m_handler != nullptr) { d->m_handler->handleSequencerEvent(event->clone()); } else { // second, process the event listeners if (d->m_eventsEnabled) { QObjectList::Iterator it; for(it=d->m_listeners.begin(); it!=d->m_listeners.end(); ++it) { QObject* sub = (*it); QCoreApplication::postEvent(sub, event->clone()); } } else { // finally, process signals Q_EMIT eventReceived(event->clone()); } } delete event; } } while (snd_seq_event_input_pending(d->m_SeqHandle, 0) > 0); } /** * Starts reading events from the ALSA sequencer. */ void MidiClient::startSequencerInput() { if (d->m_Thread == nullptr) { d->m_Thread = new SequencerInputThread(this, DEFAULT_INPUT_TIMEOUT); } d->m_Thread->start( d->m_Thread->m_RealTime ? QThread::TimeCriticalPriority : QThread::InheritPriority ); } /** * Stops reading events from the ALSA sequencer. */ void MidiClient::stopSequencerInput() { int counter = 0; if (d->m_Thread != nullptr) { if (d->m_Thread->isRunning()) { d->m_Thread->stop(); while (!d->m_Thread->wait(500) && (counter < 10)) { counter++; } if (!d->m_Thread->isFinished()) { d->m_Thread->terminate(); } } delete d->m_Thread; } } /** * Reads the ALSA sequencer's clients list. */ void MidiClient::readClients() { ClientInfo cInfo; freeClients(); cInfo.setClient(-1); while (snd_seq_query_next_client(d->m_SeqHandle, cInfo.m_Info) >= 0) { cInfo.readPorts(this); d->m_ClientList.append(cInfo); } d->m_NeedRefreshClientList = false; } /** * Releases the list of ALSA sequencer's clients. */ void MidiClient::freeClients() { d->m_ClientList.clear(); } /** * Gets the list of clients from the ALSA sequencer. * @return the list of clients. */ ClientInfoList MidiClient::getAvailableClients() { if (d->m_NeedRefreshClientList) readClients(); ClientInfoList lst = d->m_ClientList; // copy return lst; } /** * Gets the ClientInfo object holding data about this client. * @return the ClientInfo object representing this client. */ ClientInfo& MidiClient::getThisClientInfo() { snd_seq_get_client_info(d->m_SeqHandle, d->m_Info.m_Info); return d->m_Info; } /** * Sets the data supplied by the ClientInfo object into the ALSA sequencer * client. This allows to change the name, capabilities, type and other data * in a single step. * * @param val a ClientInfo object reference */ void MidiClient::setThisClientInfo(const ClientInfo& val) { d->m_Info = val; snd_seq_set_client_info(d->m_SeqHandle, d->m_Info.m_Info); } /** * This internal method applies the ClientInfo data to the ALSA sequencer client */ void MidiClient::applyClientInfo() { if (d->m_SeqHandle != nullptr) { snd_seq_set_client_info(d->m_SeqHandle, d->m_Info.m_Info); } } /** * Gets the client's public name * @return The client's name */ QString MidiClient::getClientName() { return d->m_Info.getName(); } /** * Gets the public name corresponding to the given Client ID. * @param clientId The ID of any existing sequencer client * @return The client's name */ QString MidiClient::getClientName(const int clientId) { ClientInfoList::Iterator it; if (d->m_NeedRefreshClientList) readClients(); for (it = d->m_ClientList.begin(); it != d->m_ClientList.end(); ++it) { if ((*it).getClientId() == clientId) { return (*it).getName(); } } return QString(); } /** * Changes the public name of the ALSA sequencer client. * @param newName A new public name */ void MidiClient::setClientName(QString const& newName) { if (newName != d->m_Info.getName()) { d->m_Info.setName(newName); applyClientInfo(); } } /** * Gets the list of MidiPort instances belonging to this client. * @return The list of MidiPort instances. */ MidiPortList MidiClient::getMidiPorts() const { return d->m_Ports; } /** * Create and attach a new MidiPort instance to this client. * @return The pointer to the new MidiPort instance. */ MidiPort* MidiClient::createPort() { MidiPort* port = new MidiPort(this); port->attach(this); return port; } /** * Attach a MidiPort instance to this client * @param port The MidiPort to be attached */ void MidiClient::portAttach(MidiPort* port) { if (d->m_SeqHandle != nullptr) { DRUMSTICK_ALSA_CHECK_ERROR(snd_seq_create_port(d->m_SeqHandle, port->m_Info.m_Info)); d->m_Ports.push_back(port); } } /** * Detach a MidiPort instance from this client * @param port The MidiPort to be detached */ void MidiClient::portDetach(MidiPort* port) { if (d->m_SeqHandle != nullptr) { if(port->getPortInfo()->getClient() == getClientId()) { return; } DRUMSTICK_ALSA_CHECK_ERROR(snd_seq_delete_port(d->m_SeqHandle, port->getPortInfo()->getPort())); port->setMidiClient(nullptr); MidiPortList::iterator it; for(it = d->m_Ports.begin(); it != d->m_Ports.end(); ++it) { if ((*it)->getPortInfo()->getPort() == port->getPortInfo()->getPort()) { d->m_Ports.erase(it); break; } } } } /** * Detach all the ports belonging to this client. */ void MidiClient::detachAllPorts() { if (d->m_SeqHandle != nullptr) { QMutableListIterator it(d->m_Ports); while (it.hasNext()) { MidiPort* p = it.next(); DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_delete_port(d->m_SeqHandle, p->getPortInfo()->getPort())); p->setMidiClient(nullptr); it.remove(); } } } /** * Add an event filter to the client. * @param evtype An event filter to be added. */ void MidiClient::addEventFilter(int evtype) { snd_seq_set_client_event_filter(d->m_SeqHandle, evtype); } /** * Gets the broadcast filter usage of the client. * * @return The broadcast filter. */ bool MidiClient::getBroadcastFilter() { return d->m_Info.getBroadcastFilter(); } /** * Sets the broadcast filter usage of the client. * * @param newValue The broadcast filter. */ void MidiClient::setBroadcastFilter(bool newValue) { d->m_Info.setBroadcastFilter(newValue); applyClientInfo(); } /** * Get the error-bounce usage of the client. * * @return The error-bounce usage. */ bool MidiClient::getErrorBounce() { return d->m_Info.getErrorBounce(); } /** * Sets the error-bounce usage of the client. * * @param newValue The error-bounce usage. */ void MidiClient::setErrorBounce(bool newValue) { d->m_Info.setErrorBounce(newValue); applyClientInfo(); } /** * Output an event using the library output buffer. * * An event is once expanded on the output buffer. The output buffer will be * drained automatically if it becomes full. * * @param ev The event to be sent. * @param async Use asynchronous mode. If false, this call will block until the * event can be delivered. * @param timeout The maximum time to wait in synchronous mode. */ void MidiClient::output(SequencerEvent* ev, bool async, int timeout) { pollfd* pfds = nullptr; if (async) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_event_output(d->m_SeqHandle, ev->getHandle())); } else { int npfds = snd_seq_poll_descriptors_count(d->m_SeqHandle, POLLOUT); pfds = (pollfd*) calloc(npfds, sizeof(pollfd)); snd_seq_poll_descriptors(d->m_SeqHandle, pfds, npfds, POLLOUT); while (snd_seq_event_output(d->m_SeqHandle, ev->getHandle()) < 0) { poll(pfds, npfds, timeout); } free(pfds); } } /** * Output an event directly to the sequencer * * This function sends an event to the sequencer directly not using the library * output buffer. * * @param ev The event to be sent. * @param async Use asynchronous mode. If false, this call will block until the * event is delivered to the sequencer. * @param timeout The maximum time to wait in synchronous mode. */ void MidiClient::outputDirect(SequencerEvent* ev, bool async, int timeout) { if (async) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_event_output_direct(d->m_SeqHandle, ev->getHandle())); } else { int npfds = snd_seq_poll_descriptors_count(d->m_SeqHandle, POLLOUT); pollfd* pfds = (pollfd*) calloc(npfds, sizeof(pollfd)); snd_seq_poll_descriptors(d->m_SeqHandle, pfds, npfds, POLLOUT); while (snd_seq_event_output_direct(d->m_SeqHandle, ev->getHandle()) < 0) { poll(pfds, npfds, timeout); } free(pfds); } } /** * Output an event using the library output buffer, without draining the buffer. * * An event is once expanded on the output buffer. The output buffer will NOT be * drained automatically if it becomes full. * * @param ev The event to be sent. */ void MidiClient::outputBuffer(SequencerEvent* ev) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_event_output_buffer(d->m_SeqHandle, ev->getHandle())); } /** * Drain the library output buffer. * * This function drains all pending events on the output buffer. The function * returns immediately after the events are sent to the queues regardless * whether the events are processed or not. * * @param async Use asynchronous mode. If false, this call will block until the * buffer can be flushed. * @param timeout The maximum time to wait in synchronous mode. */ void MidiClient::drainOutput(bool async, int timeout) { if (async) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_drain_output(d->m_SeqHandle)); } else { int npfds = snd_seq_poll_descriptors_count(d->m_SeqHandle, POLLOUT); pollfd* pfds = (pollfd*) calloc(npfds, sizeof(pollfd)); snd_seq_poll_descriptors(d->m_SeqHandle, pfds, npfds, POLLOUT); while (snd_seq_drain_output(d->m_SeqHandle) < 0) { poll(pfds, npfds, timeout); } free(pfds); } } /** * Wait until all sent events are processed. * * This function waits until all events of this client are processed. */ void MidiClient::synchronizeOutput() { snd_seq_sync_output_queue(d->m_SeqHandle); } /** * Get the MidiQueue instance associated to this client. * If the client is not associated to a MidiQueue, one is created. * @return A MidiQueue instance pointer */ MidiQueue* MidiClient::getQueue() { if (d->m_Queue == nullptr) { createQueue(); } return d->m_Queue; } /** * Create and return a new MidiQueue associated to this client. * @return A new MidiQueue instance. */ MidiQueue* MidiClient::createQueue() { if (d->m_Queue != nullptr) { delete d->m_Queue; } d->m_Queue = new MidiQueue(this, this); return d->m_Queue; } /** * Create and return a new MidiQueue with the given name, associated to this * client. * @param queueName The name for the new queue. * @return A new MidiQueue instance. */ MidiQueue* MidiClient::createQueue(QString const& queueName ) { if (d->m_Queue != nullptr) { delete d->m_Queue; } d->m_Queue = new MidiQueue(this, queueName, this); return d->m_Queue; } /** * Create a new MidiQueue instance using a queue already existing in the * system, associating it to the client. * * @param queue_id An existing queue identifier. * @return A new MidiQueue instance. */ MidiQueue* MidiClient::useQueue(int queue_id) { if (d->m_Queue != nullptr) { delete d->m_Queue; } d->m_Queue = new MidiQueue(this, queue_id, this); return d->m_Queue; } /** * Create a new MidiQueue instance using a queue already existing in the * system, associating it to the client. * * @param name An existing queue name. * @return A new MidiQueue instance. */ MidiQueue* MidiClient::useQueue(const QString& name) { if (d->m_Queue != nullptr) { delete d->m_Queue; } int queue_id = getQueueId(name); if ( queue_id >= 0) { d->m_Queue = new MidiQueue(this, queue_id, this); } return d->m_Queue; } /** * Associate an existing MidiQueue instance to the client. * * @param queue An existing MidiQueue. * @return The provided MidiQueue instance. */ MidiQueue* MidiClient::useQueue(MidiQueue* queue) { if (d->m_Queue != nullptr) { delete d->m_Queue; } queue->setParent(this); d->m_Queue = queue; return d->m_Queue; } /** * Get a list of the existing queues * @return a list of existing queues */ QList MidiClient::getAvailableQueues() { int q, err, max; QList queues; snd_seq_queue_info_t* qinfo; snd_seq_queue_info_alloca(&qinfo); max = getSystemInfo().getMaxQueues(); for ( q = 0; q < max; ++q ) { err = snd_seq_get_queue_info(d->m_SeqHandle, q, qinfo); if (err == 0) { queues.append(q); } } return queues; } /** * Gets a list of the available user ports in the system, filtered by the given * bitmap of desired capabilities. * * @param filter A bitmap of capabilities. * @return A filtered list of the available ports in the system. */ PortInfoList MidiClient::filterPorts(unsigned int filter) { PortInfoList result; ClientInfoList::ConstIterator itc; PortInfoList::ConstIterator itp; if (d->m_NeedRefreshClientList) readClients(); for (itc = d->m_ClientList.constBegin(); itc != d->m_ClientList.constEnd(); ++itc) { ClientInfo ci = (*itc); if ((ci.getClientId() == SND_SEQ_CLIENT_SYSTEM) || (ci.getClientId() == d->m_Info.getClientId())) continue; PortInfoList lstPorts = ci.getPorts(); for(itp = lstPorts.constBegin(); itp != lstPorts.constEnd(); ++itp) { PortInfo pi = (*itp); unsigned int cap = pi.getCapability(); if ( ((filter & cap) != 0) && ((SND_SEQ_PORT_CAP_NO_EXPORT & cap) == 0) ) { result.append(pi); } } } return result; } /** * Update the internal lists of user ports. */ void MidiClient::updateAvailablePorts() { d->m_InputsAvail.clear(); d->m_OutputsAvail.clear(); d->m_InputsAvail = filterPorts( SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ ); d->m_OutputsAvail = filterPorts( SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE ); } /** * Gets the available user input ports in the system. * @return The list of available input ports. */ PortInfoList MidiClient::getAvailableInputs() { d->m_NeedRefreshClientList = true; updateAvailablePorts(); return d->m_InputsAvail; } /** * Gets the available user output ports in the system. * @return The list of available output ports. */ PortInfoList MidiClient::getAvailableOutputs() { d->m_NeedRefreshClientList = true; updateAvailablePorts(); return d->m_OutputsAvail; } /** * Adds a QObject to the listeners list. This object should override the method * QObject::customEvent() to receive SequencerEvent instances. * @param listener A QObject listener to be notified of received events. * @see removeListener(), setEventsEnabled() */ void MidiClient::addListener(QObject* listener) { d->m_listeners.append(listener); } /** * Removes a QObject listener from the listeners list. * @param listener listener A QObject listener to be removed of received events. * @see addListener(), setEventsEnabled() */ void MidiClient::removeListener(QObject* listener) { d->m_listeners.removeAll(listener); } /** * Enables the notification of received SequencerEvent instances to the listeners * registered with addListener() * @param bEnabled The new state of the events delivering mode. * @see addListener(), removeListener(), setEventsEnabled() */ void MidiClient::setEventsEnabled(bool bEnabled) { if (bEnabled != d->m_eventsEnabled) { d->m_eventsEnabled = bEnabled; } } /** * Gets a SystemInfo instance with the updated state of the system. * @return The updated system info. */ SystemInfo& MidiClient::getSystemInfo() { snd_seq_system_info(d->m_SeqHandle, d->m_sysInfo.m_Info); return d->m_sysInfo; } /** * Gets a PoolInfo instance with an updated state of the client memory pool * @return The updated memory pool state. */ PoolInfo& MidiClient::getPoolInfo() { snd_seq_get_client_pool(d->m_SeqHandle, d->m_poolInfo.m_Info); return d->m_poolInfo; } /** * Applies (updates) the client's PoolInfo data into the system. * @param info The PoolInfo reference to be applied to the client. */ void MidiClient::setPoolInfo(const PoolInfo& info) { d->m_poolInfo = info; DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_client_pool(d->m_SeqHandle, d->m_poolInfo.m_Info)); } /** * Resets the client input pool. * @see dropInput() */ void MidiClient::resetPoolInput() { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_reset_pool_input(d->m_SeqHandle)); } /** * Resets the client output pool. * @see dropOutput() */ void MidiClient::resetPoolOutput() { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_reset_pool_output(d->m_SeqHandle)); } /** * Sets the size of the client's input pool. * @param size The new size */ void MidiClient::setPoolInput(int size) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_client_pool_input(d->m_SeqHandle, size)); } /** * Sets the size of the client's output pool. * @param size The new size */ void MidiClient::setPoolOutput(int size) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_client_pool_output(d->m_SeqHandle, size)); } /** * Sets the room size of the client's output pool. * @param size The new size */ void MidiClient::setPoolOutputRoom(int size) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_client_pool_output_room(d->m_SeqHandle, size)); } /** * Clears the client's input buffer and and remove events in sequencer queue. * @see resetPoolInput() */ void MidiClient::dropInput() { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_drop_input(d->m_SeqHandle)); } /** * Remove all events on user-space input buffer. * @see dropInput() */ void MidiClient::dropInputBuffer() { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_drop_input_buffer(d->m_SeqHandle)); } /** * Clears the client's output buffer and and remove events in sequencer queue. * * This method removes all events on both user-space output buffer and output * memory pool on kernel. * @see resetPoolOutput() */ void MidiClient::dropOutput() { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_drop_output(d->m_SeqHandle)); } /** * Removes all events on the library output buffer. * * Removes all events on the user-space output buffer. Unlike dropOutput(), this * method doesn't remove events on the client's output memory pool. * @see dropOutput() */ void MidiClient::dropOutputBuffer() { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_drop_output_buffer(d->m_SeqHandle)); } /** * Removes events on input/output buffers and pools. * Removes matching events with the given condition from input/output buffers * and pools. The removal condition is specified in the spec argument. * @param spec A RemoveEvents instance specifying the removal condition. */ void MidiClient::removeEvents(const RemoveEvents* spec) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_remove_events(d->m_SeqHandle, spec->m_Info)); } /** * Extracts (and removes) the first event in the output buffer. * @return The extracted event. */ SequencerEvent* MidiClient::extractOutput() { snd_seq_event_t* ev; if (DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_extract_output(d->m_SeqHandle, &ev) == 0)) { return new SequencerEvent(ev); } return nullptr; } /** * Returns the size of pending events on the output buffer. * * @return The size of pending events. */ int MidiClient::outputPending() { return snd_seq_event_output_pending(d->m_SeqHandle); } /** * Gets the size of the events on the input buffer. * * If there are events remaining on the user-space input buffer, this method * returns the total size of events on it. If the argument is true, this method * checks the presence of events on the sequencer FIFO, and when events exist * they are transferred to the input buffer, and the number of received events * are returned. If the argument is false and no events remain on the input * buffer, this method simply returns zero. * * @param fetch Check and fetch the sequencer input pool. * @return The size in bytes of the remaining input events on the buffer. */ int MidiClient::inputPending(bool fetch) { return snd_seq_event_input_pending(d->m_SeqHandle, fetch ? 1 : 0); } /** * Gets the queue's numeric identifier corresponding to the provided name. * * @param name The name string to query. * @return The number of the matching queue. */ int MidiClient::getQueueId(const QString& name) { return snd_seq_query_named_queue(d->m_SeqHandle, name.toLocal8Bit().data()); } /** * Returns the number of poll descriptors. * @param events Poll events to be checked (POLLIN and POLLOUT). * @return The number of poll descriptors. */ int MidiClient::getPollDescriptorsCount(short events) { return snd_seq_poll_descriptors_count(d->m_SeqHandle, events); } /** * Get poll descriptors. * * Get poll descriptors assigned to the sequencer handle. Since a sequencer * handle can duplex streams, you need to set which direction(s) is/are polled * in events argument. When POLLIN bit is specified, the incoming events to the * ports are checked. * * @param pfds Array of poll descriptors * @param space Space in the poll descriptor array * @param events Polling events to be checked (POLLIN and POLLOUT) * @return Count of filled descriptors */ int MidiClient::pollDescriptors( struct pollfd *pfds, unsigned int space, short events ) { return snd_seq_poll_descriptors(d->m_SeqHandle, pfds, space, events); } /** * Gets the number of returned events from poll descriptors * @param pfds Array of poll descriptors. * @param nfds Count of poll descriptors. * @return Number of returned events. */ unsigned short MidiClient::pollDescriptorsRevents(struct pollfd *pfds, unsigned int nfds) { unsigned short revents; DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_poll_descriptors_revents( d->m_SeqHandle, pfds, nfds, &revents )); return revents; } /** * Gets the internal sequencer device name * @return The device name. */ const char * MidiClient::_getDeviceName() { return snd_seq_name(d->m_SeqHandle); } /** * Sets the client name * @param name The new client name. */ void MidiClient::_setClientName(const char *name) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_client_name(d->m_SeqHandle, name)); } /** * Create an ALSA sequencer port, without using MidiPort. * @param name The name of the new port. * @param caps The new port capabilities. * @param type The type of the new port. * @return The port numeric identifier. */ int MidiClient::createSimplePort( const char *name, unsigned int caps, unsigned int type ) { return DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_create_simple_port( d->m_SeqHandle, name, caps, type )); } /** * Remove an ALSA sequencer port. * @param port The numeric identifier of the port. */ void MidiClient::deleteSimplePort(int port) { DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_delete_simple_port( d->m_SeqHandle, port )); } /** * Subscribe one port from another arbitrary sequencer client:port. * @param myport The number of the internal port. * @param client The external client's identifier. * @param port The external port's identifier. */ void MidiClient::connectFrom(int myport, int client, int port) { DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_connect_from(d->m_SeqHandle, myport, client, port )); } /** * Subscribe one port to another arbitrary sequencer client:port. * @param myport The number of the internal port. * @param client The external client's identifier. * @param port The external port's identifier. */ void MidiClient::connectTo(int myport, int client, int port) { DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_connect_to(d->m_SeqHandle, myport, client, port )); } /** * Unsubscribe one port from another arbitrary sequencer client:port. * @param myport The number of the internal port. * @param client The external client's identifier. * @param port The external port's identifier. */ void MidiClient::disconnectFrom(int myport, int client, int port) { DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_disconnect_from(d->m_SeqHandle, myport, client, port )); } /** * Unsubscribe one port to another arbitrary sequencer client:port. * @param myport The number of the internal port. * @param client The external client's identifier. * @param port The external port's identifier. */ void MidiClient::disconnectTo(int myport, int client, int port) { DRUMSTICK_ALSA_CHECK_WARNING( snd_seq_disconnect_to(d->m_SeqHandle, myport, client, port )); } /** * Parse a text address representation, returning an ALSA address record. * * This function can be used as a replacement of the standard ALSA function * snd_seq_parse_address(). * * @param straddr source text address representation * @param addr returned ALSA address record * @return true if the text address was successfully parsed * @since 0.3.1 */ bool MidiClient::parseAddress( const QString& straddr, snd_seq_addr& addr ) { bool ok(false); QString testClient, testPort; ClientInfoList::ConstIterator cit; int pos = straddr.indexOf(':'); if (pos > -1) { testClient = straddr.left(pos); testPort = straddr.mid(pos+1); } else { testClient = straddr; testPort = '0'; } addr.client = testClient.toInt(&ok); if (ok) addr.port = testPort.toInt(&ok); if (!ok) { if (d->m_NeedRefreshClientList) readClients(); for ( cit = d->m_ClientList.constBegin(); cit != d->m_ClientList.constEnd(); ++cit ) { ClientInfo ci = *cit; if (testClient.compare(ci.getName(), Qt::CaseInsensitive) == 0) { addr.client = ci.getClientId(); addr.port = testPort.toInt(&ok); return ok; } } } return ok; } /** * Returns true or false depending on the input thread state. * @return true if the input thread is stopped. */ bool MidiClient::SequencerInputThread::stopped() { QReadLocker locker(&m_mutex); return m_Stopped; } /** * Stops the input thread. */ void MidiClient::SequencerInputThread::stop() { QWriteLocker locker(&m_mutex); m_Stopped = true; } #if defined(RTKIT_SUPPORT) static pid_t _gettid() { return (pid_t) ::syscall(SYS_gettid); } #endif void MidiClient::SequencerInputThread::setRealtimePriority() { struct sched_param p; int rt, policy = SCHED_RR | SCHED_RESET_ON_FORK; quint32 priority = 6; #if defined(RTKIT_SUPPORT) bool ok; quint32 max_prio; quint64 thread; struct rlimit old_limit, new_limit; long long max_rttime; #endif ::memset(&p, 0, sizeof(p)); p.sched_priority = priority; rt = ::pthread_setschedparam(::pthread_self(), policy, &p); if (rt != 0) { #if defined(RTKIT_SUPPORT) const QString rtkit_service = QStringLiteral("org.freedesktop.RealtimeKit1"); const QString rtkit_path = QStringLiteral("/org/freedesktop/RealtimeKit1"); const QString rtkit_iface = rtkit_service; thread = _gettid(); QDBusConnection bus = QDBusConnection::systemBus(); QDBusInterface realtimeKit(rtkit_service, rtkit_path, rtkit_iface, bus); QVariant maxRTPrio = realtimeKit.property("MaxRealtimePriority"); max_prio = maxRTPrio.toUInt(&ok); if (!ok) { qWarning() << "invalid property RealtimeKit.MaxRealtimePriority"; return; } if (priority > max_prio) priority = max_prio; QVariant maxRTNSec = realtimeKit.property("RTTimeNSecMax"); max_rttime = maxRTNSec.toLongLong(&ok); if (!ok || max_rttime < 0) { qWarning() << "invalid property RealtimeKit.RTTimeNSecMax"; return; } new_limit.rlim_cur = new_limit.rlim_max = max_rttime; rt = ::getrlimit(RLIMIT_RTTIME, &old_limit); if (rt < 0) { qWarning() << "getrlimit() failed. err=" << rt << ::strerror(rt); return; } rt = ::setrlimit(RLIMIT_RTTIME, &new_limit); if ( rt < 0) { qWarning() << "setrlimit() failed, err=" << rt << ::strerror(rt); return; } QDBusMessage reply = realtimeKit.call("MakeThreadRealtime", thread, priority); if (reply.type() == QDBusMessage::ErrorMessage ) qWarning() << "error returned by RealtimeKit.MakeThreadRealtime:" << reply.errorMessage(); #endif } else { qWarning() << "pthread_setschedparam() failed, err=" << rt << ::strerror(rt); } } /** * Main input thread process loop. */ void MidiClient::SequencerInputThread::run() { if ( priority() == TimeCriticalPriority ) { setRealtimePriority(); } if (m_MidiClient != nullptr) { int npfd = snd_seq_poll_descriptors_count(m_MidiClient->getHandle(), POLLIN); pollfd* pfd = (pollfd *) calloc(npfd, sizeof(pollfd)); try { snd_seq_poll_descriptors(m_MidiClient->getHandle(), pfd, npfd, POLLIN); while (!stopped() && (m_MidiClient != nullptr)) { int rt = poll(pfd, npfd, m_Wait); if (rt > 0) { m_MidiClient->doEvents(); } } } catch (...) { qWarning() << "exception in input thread"; } free(pfd); } } /** * Default constructor */ ClientInfo::ClientInfo() { snd_seq_client_info_malloc(&m_Info); } /** * Copy constructor * @param other Another ClientInfo reference to be copied */ ClientInfo::ClientInfo(const ClientInfo& other) { snd_seq_client_info_malloc(&m_Info); snd_seq_client_info_copy(m_Info, other.m_Info); m_Ports = other.m_Ports; } /** * Copy constructor * @param other An existing ALSA client info object */ ClientInfo::ClientInfo(snd_seq_client_info_t* other) { snd_seq_client_info_malloc(&m_Info); snd_seq_client_info_copy(m_Info, other); } /** * Constructor * @param seq A MidiClient object * @param id A numeric client id */ ClientInfo::ClientInfo(MidiClient* seq, int id) { snd_seq_client_info_malloc(&m_Info); snd_seq_get_any_client_info(seq->getHandle(), id, m_Info); } /** * Destructor */ ClientInfo::~ClientInfo() { freePorts(); snd_seq_client_info_free(m_Info); } /** * Clone the client info object. * @return A pointer to the new object. */ ClientInfo* ClientInfo::clone() { return new ClientInfo(m_Info); } /** * Assignment operator * @param other Another ClientInfo object * @return This object */ ClientInfo& ClientInfo::operator=(const ClientInfo& other) { if (this == &other) return *this; snd_seq_client_info_copy(m_Info, other.m_Info); m_Ports = other.m_Ports; return *this; } /** * Gets the client's numeric identifier. * @return The client's numeric identifier. */ int ClientInfo::getClientId() { return snd_seq_client_info_get_client(m_Info); } /** * Gets the client's type * @return The client's type. */ snd_seq_client_type_t ClientInfo::getClientType() { return snd_seq_client_info_get_type(m_Info); } /** * Gets the client's name * @return The client's name. */ QString ClientInfo::getName() { return QString(snd_seq_client_info_get_name(m_Info)); } /** * Gets the client's broadcast filter * @return The client's broadcast filter. */ bool ClientInfo::getBroadcastFilter() { return (snd_seq_client_info_get_broadcast_filter(m_Info) != 0); } /** * Gets the client's error bounce * @return The client's error bounce. */ bool ClientInfo::getErrorBounce() { return (snd_seq_client_info_get_error_bounce(m_Info) != 0); } /** * Gets the client's event filter. * @return The client's event filter. * @deprecated Use isFiltered() instead. */ const unsigned char* ClientInfo::getEventFilter() { return snd_seq_client_info_get_event_filter(m_Info); } /** * Gets the client's port count. * @return The client's port count. */ int ClientInfo::getNumPorts() { return snd_seq_client_info_get_num_ports(m_Info); } /** * Gets the number of lost events. * @return The number of lost events. */ int ClientInfo::getEventLost() { return snd_seq_client_info_get_event_lost(m_Info); } /** * Sets the client identifier number. * @param client The client identifier number. */ void ClientInfo::setClient(int client) { snd_seq_client_info_set_client(m_Info, client); } /** * Sets the client name. * @param name The client name. */ void ClientInfo::setName(QString name) { snd_seq_client_info_set_name(m_Info, name.toLocal8Bit().data()); } /** * Sets the broadcast filter. * @param val The broadcast filter. */ void ClientInfo::setBroadcastFilter(bool val) { snd_seq_client_info_set_broadcast_filter(m_Info, val ? 1 : 0); } /** * Sets the error bounce. * @param val The error bounce. */ void ClientInfo::setErrorBounce(bool val) { snd_seq_client_info_set_error_bounce(m_Info, val ? 1 : 0); } /** * Sets the event filter. * @param filter The event filter. * @deprecated Use addFilter() instead. */ void ClientInfo::setEventFilter(unsigned char *filter) { snd_seq_client_info_set_event_filter(m_Info, filter); } /** * Read the client ports. * @param seq The client instance. */ void ClientInfo::readPorts(MidiClient* seq) { PortInfo info; freePorts(); info.setClient(getClientId()); info.setClientName(getName()); info.setPort(-1); while (snd_seq_query_next_port(seq->getHandle(), info.m_Info) >= 0) { info.readSubscribers(seq); m_Ports.append(info); } } /** * Release the ports list. */ void ClientInfo::freePorts() { m_Ports.clear(); } /** * Gets the ports list. * @return The ports list. */ PortInfoList ClientInfo::getPorts() const { PortInfoList lst = m_Ports; // copy return lst; } /** * Gets the size of the internal object. * @return The size of the internal object. */ int ClientInfo::getSizeOfInfo() const { return snd_seq_client_info_sizeof(); } #if SND_LIB_VERSION > 0x010010 /** * Adds an event type to the client's filter. * * @param eventType The new event's type. */ void ClientInfo::addFilter(int eventType) { snd_seq_client_info_event_filter_add(m_Info, eventType); } /** * Checks id the given event's type is filtered. * @param eventType The event's type. * @return true if the event type is filtered */ bool ClientInfo::isFiltered(int eventType) { return (snd_seq_client_info_event_filter_check(m_Info, eventType) != 0); } /** * Clear the client's event filter */ void ClientInfo::clearFilter() { snd_seq_client_info_event_filter_clear(m_Info); } /** * Removes the event type from the client's filter. * @param eventType The event's type. */ void ClientInfo::removeFilter(int eventType) { snd_seq_client_info_event_filter_del(m_Info, eventType); } #endif /** * Default constructor */ SystemInfo::SystemInfo() { snd_seq_system_info_malloc(&m_Info); } /** * Copy constructor * @param other Another SystemInfo object reference to be copied */ SystemInfo::SystemInfo(const SystemInfo& other) { snd_seq_system_info_malloc(&m_Info); snd_seq_system_info_copy(m_Info, other.m_Info); } /** * Copy constructor * @param other Another ALSA system info object to be copied */ SystemInfo::SystemInfo(snd_seq_system_info_t* other) { snd_seq_system_info_malloc(&m_Info); snd_seq_system_info_copy(m_Info, other); } /** * Constructor * @param seq A MidiClient object */ SystemInfo::SystemInfo(MidiClient* seq) { snd_seq_system_info_malloc(&m_Info); snd_seq_system_info(seq->getHandle(), m_Info); } /** * Destructor */ SystemInfo::~SystemInfo() { snd_seq_system_info_free(m_Info); } /** * Clone the system info object * @return A pointer to the new object */ SystemInfo* SystemInfo::clone() { return new SystemInfo(m_Info); } /** * Assignment operator * @param other Another SystemInfo object * @return This object */ SystemInfo& SystemInfo::operator=(const SystemInfo& other) { if (this == &other) return *this; snd_seq_system_info_copy(m_Info, other.m_Info); return *this; } /** * Get the system's maximum number of clients. * @return The maximum number of clients. */ int SystemInfo::getMaxClients() { return snd_seq_system_info_get_clients(m_Info); } /** * Get the system's maximum number of ports. * @return The maximum number of ports. */ int SystemInfo::getMaxPorts() { return snd_seq_system_info_get_ports(m_Info); } /** * Get the system's maximum number of queues. * @return The system's maximum number of queues. */ int SystemInfo::getMaxQueues() { return snd_seq_system_info_get_queues(m_Info); } /** * Get the system's maximum number of channels. * @return The system's maximum number of channels. */ int SystemInfo::getMaxChannels() { return snd_seq_system_info_get_channels(m_Info); } /** * Get the system's current number of queues. * @return The system's current number of queues. */ int SystemInfo::getCurrentQueues() { return snd_seq_system_info_get_cur_queues(m_Info); } /** * Get the system's current number of clients. * @return The system's current number of clients. */ int SystemInfo::getCurrentClients() { return snd_seq_system_info_get_cur_clients(m_Info); } /** * Get the system's info object size. * @return The system's info object size. */ int SystemInfo::getSizeOfInfo() const { return snd_seq_system_info_sizeof(); } /** * Default constructor */ PoolInfo::PoolInfo() { snd_seq_client_pool_malloc(&m_Info); } /** * Copy constructor * @param other Another PoolInfo object reference to be copied */ PoolInfo::PoolInfo(const PoolInfo& other) { snd_seq_client_pool_malloc(&m_Info); snd_seq_client_pool_copy(m_Info, other.m_Info); } /** * Copy constructor * @param other An ALSA pool info object to be copied */ PoolInfo::PoolInfo(snd_seq_client_pool_t* other) { snd_seq_client_pool_malloc(&m_Info); snd_seq_client_pool_copy(m_Info, other); } /** * Constructor * @param seq A MidiClient object */ PoolInfo::PoolInfo(MidiClient* seq) { snd_seq_client_pool_malloc(&m_Info); snd_seq_get_client_pool(seq->getHandle(), m_Info); } /** * Destructor */ PoolInfo::~PoolInfo() { snd_seq_client_pool_free(m_Info); } /** * Clone the pool info obeject * @return A pointer to the new object */ PoolInfo* PoolInfo::clone() { return new PoolInfo(m_Info); } /** * Assignment operator * @param other Another PoolInfo object reference to be copied * @return This object */ PoolInfo& PoolInfo::operator=(const PoolInfo& other) { if (this == &other) return *this; snd_seq_client_pool_copy(m_Info, other.m_Info); return *this; } /** * Gets the client ID for this object. * @return The client ID. */ int PoolInfo::getClientId() { return snd_seq_client_pool_get_client(m_Info); } /** * Gets the available size on input pool. * @return The available size on input pool. */ int PoolInfo::getInputFree() { return snd_seq_client_pool_get_input_free(m_Info); } /** * Gets the input pool size. * @return The input pool size. */ int PoolInfo::getInputPool() { return snd_seq_client_pool_get_input_pool(m_Info); } /** * Gets the available size on output pool. * @return The available size on output pool. */ int PoolInfo::getOutputFree() { return snd_seq_client_pool_get_output_free(m_Info); } /** * Gets the output pool size. * @return The output pool size. */ int PoolInfo::getOutputPool() { return snd_seq_client_pool_get_output_pool(m_Info); } /** * Gets the output room size. * The output room is the minimum pool size for select/blocking mode. * @return The output room size. */ int PoolInfo::getOutputRoom() { return snd_seq_client_pool_get_output_room(m_Info); } /** * Set the input pool size. * @param size The input pool size. */ void PoolInfo::setInputPool(int size) { snd_seq_client_pool_set_input_pool(m_Info, size); } /** * Sets the output pool size. * @param size The output pool size. */ void PoolInfo::setOutputPool(int size) { snd_seq_client_pool_set_output_pool(m_Info, size); } /** * Sets the output room size. * The output room is the minimum pool size for select/blocking mode. * * @param size Output room size */ void PoolInfo::setOutputRoom(int size) { snd_seq_client_pool_set_output_room(m_Info, size); } /** * Gets the size of the client pool object. * @return The size of the client pool object. */ int PoolInfo::getSizeOfInfo() const { return snd_seq_client_pool_sizeof(); } #if SND_LIB_VERSION > 0x010004 /** * Gets the runtime ALSA library version string * @return string representing the runtime ALSA library version * @since 0.3.0 */ QString getRuntimeALSALibraryVersion() { return QString(snd_asoundlib_version()); } /** * Gets the runtime ALSA library version number * @return integer representing the runtime ALSA library version * @since 0.3.0 */ int getRuntimeALSALibraryNumber() { QRegularExpression rx("(\\d+)"); QString str = getRuntimeALSALibraryVersion(); bool ok; int result = 0, j = 0; QRegularExpressionMatchIterator i = rx.globalMatch(str); while (i.hasNext() && (j < 3)) { QRegularExpressionMatch m = i.next(); int v = m.captured(1).toInt(&ok); if (ok) { result <<= 8; result += v; } j++; } return result; } #endif // SND_LIB_VERSION > 0x010004 /** * Gets the runtime ALSA drivers version string * @return string representing the runtime ALSA drivers version * @since 0.3.0 */ QString getRuntimeALSADriverVersion() { QRegularExpression rx("([\\d\\.]+)"); QString s; QFile f("/proc/asound/version"); if (f.open(QFile::ReadOnly)) { QTextStream str(&f); QString sub = str.readLine().trimmed(); QRegularExpressionMatch m = rx.match(sub); if (m.hasMatch()) { s = m.captured(1); } } return s; } /** * Gets the runtime ALSA drivers version number * @return integer representing the runtime ALSA drivers version * @since 0.3.0 */ int getRuntimeALSADriverNumber() { QRegularExpression rx("(\\d+)"); QString str = getRuntimeALSADriverVersion(); bool ok; int result = 0, j = 0; QRegularExpressionMatchIterator i = rx.globalMatch(str); while (i.hasNext() && (j < 3)) { QRegularExpressionMatch m = i.next(); int v = m.captured(1).toInt(&ok); if (ok) { result <<= 8; result += v; } j++; } return result; } /** * ALSA library version at build time. * * This string corresponds to the compilation library, which may be * different to the runtime library. * @return ALSA runtime library formatted as a QString * @see getRuntimeALSALibraryVersion */ QString getCompiledALSALibraryVersion() { return QStringLiteral(SND_LIB_VERSION_STR); } /** * @brief getDrumstickLibraryVersion provides the Drumstick version as an edited QString * @return Drumstick library version */ QString getDrumstickLibraryVersion() { return QStringLiteral(QT_STRINGIFY(VERSION)); } /** @} */ } // namespace ALSA } // namespace drumstick drumstick-2.9.0/library/alsa/alsaport.cpp0000644000175000017500000006433414541630232017462 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "errorcheck.h" #include #include /** * @file alsaport.cpp * Implementation of classes managing ALSA Sequencer ports. */ namespace drumstick { namespace ALSA { /** * @addtogroup ALSAPort * @{ * * Ports are the endpoints of the MIDI connections. * * Ports can be readable, writable, or both. They can be private for the owner * application, or can be subscribed by a third application. * * The ALSA sequencer readable ports are equivalent to the MIDI OUT ports in the * real world. Similarly, the writable ports are equivalent to the MIDI IN ones. * * Classes: * * PortInfo is a container to retrieve and change some properties about the ALSA * MIDI ports. * * MidiPort represents an ALSA MIDI port. * * @see https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_port.html * @} */ /** * Default constructor. */ PortInfo::PortInfo() { snd_seq_port_info_malloc(&m_Info); } /** * Copy constructor * @param other Another PortInfo object reference */ PortInfo::PortInfo(const PortInfo& other) { snd_seq_port_info_malloc(&m_Info); snd_seq_port_info_copy(m_Info, other.m_Info); m_ReadSubscribers = other.m_ReadSubscribers; m_WriteSubscribers = other.m_WriteSubscribers; m_ClientName = other.m_ClientName; } /** * Constructor * @param other An ALSA port info object pointer */ PortInfo::PortInfo(snd_seq_port_info_t* other) { snd_seq_port_info_malloc(&m_Info); snd_seq_port_info_copy(m_Info, other); } /** * Constructor * @param seq A MidiClient instance * @param client An existing client number * @param port An existing port number */ PortInfo::PortInfo(MidiClient* seq, const int client, const int port) { snd_seq_port_info_malloc(&m_Info); DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_get_any_port_info(seq->getHandle(), client, port, m_Info)); } /** * Constructor * @param seq A MidiClient instance * @param port An existing port number */ PortInfo::PortInfo(MidiClient* seq, const int port) { snd_seq_port_info_malloc(&m_Info); DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_get_port_info(seq->getHandle(), port, m_Info)); } /** * Destructor */ PortInfo::~PortInfo() { snd_seq_port_info_free(m_Info); freeSubscribers(); } /** * Copy the current object * @return A pointer to the new object */ PortInfo* PortInfo::clone() { return new PortInfo(m_Info); } /** * Assignment operator * @param other Another PortInfo object reference * @return This object */ PortInfo& PortInfo::operator=(const PortInfo& other) { if (this == &other) return *this; snd_seq_port_info_copy(m_Info, other.m_Info); m_ReadSubscribers = other.m_ReadSubscribers; m_WriteSubscribers = other.m_WriteSubscribers; m_ClientName = other.m_ClientName; return *this; } /** * Gets the client number * @return The client number * @see setClient() */ int PortInfo::getClient() { return snd_seq_port_info_get_client(m_Info); } /** * Gets the port number * @return The port number * @see setPort() */ int PortInfo::getPort() { return snd_seq_port_info_get_port(m_Info); } /** * Gets the client name. * @return the client name * @see setClientName() */ QString PortInfo::getClientName() const { return m_ClientName; } /** * Gets the address record for this port * @return A pointer to the address record * @see setAddr() */ const snd_seq_addr_t* PortInfo::getAddr() { return snd_seq_port_info_get_addr(m_Info); } /** * Gets the port name * @return The port name * @see setName() */ QString PortInfo::getName() { return QString(snd_seq_port_info_get_name(m_Info)); } /** * Gets the capabilities bitmap * @return The capabilities bitmap * @see setCapability() */ unsigned int PortInfo::getCapability() { return snd_seq_port_info_get_capability(m_Info); } /** * Gets the port type * @return The port type * @see setType() */ unsigned int PortInfo::getType() { return snd_seq_port_info_get_type(m_Info); } /** * Gets the MIDI channels * @return The MIDI channels * @see setMidiChannels() */ int PortInfo::getMidiChannels() { return snd_seq_port_info_get_midi_channels(m_Info); } /** * Gets the MIDI voices * @return The MIDI voices * @see setMidiVoices() */ int PortInfo::getMidiVoices() { return snd_seq_port_info_get_midi_voices(m_Info); } /** * Gets the synth voices * @return The synth voices * @see setSynthVoices() */ int PortInfo::getSynthVoices() { return snd_seq_port_info_get_synth_voices(m_Info); } /** * Get the number of read subscriptions. * @return The number of read subscriptions. */ int PortInfo::getReadUse() { return snd_seq_port_info_get_read_use(m_Info); } /** * Gets the number of write subscriptions. * @return The number of write subscriptions. */ int PortInfo::getWriteUse() { return snd_seq_port_info_get_write_use(m_Info); } /** * Gets the port-specified mode. * @return The port-specified mode. * @see setPortSpecified() */ int PortInfo::getPortSpecified() { return snd_seq_port_info_get_port_specified(m_Info); } /** * Sets the client number * @param client The client number * @see getClient() */ void PortInfo::setClient(int client) { snd_seq_port_info_set_client(m_Info, client); } /** * Set the port number * @param port The port number * @see getPort() */ void PortInfo::setPort(int port) { snd_seq_port_info_set_port(m_Info, port); } /** * Sets the address record * @param addr An address record pointer * @see getAddr() */ void PortInfo::setAddr(const snd_seq_addr_t* addr) { snd_seq_port_info_set_addr(m_Info, addr); } /** * Sets the port name * @param name The port name * @see getName() */ void PortInfo::setName(QString const& name) { snd_seq_port_info_set_name(m_Info, name.toLocal8Bit().data()); } /** * Sets the capability bitmap. * * Each port has the capability bitmaps to specify the access of * the port from other clients. The capability bit flags are: *
    *
  • SND_SEQ_PORT_CAP_READ Readable from this port
  • *
  • SND_SEQ_PORT_CAP_WRITE Writable to this port
  • *
  • SND_SEQ_PORT_CAP_DUPLEX Read/write duplex access is supported
  • *
  • SND_SEQ_PORT_CAP_SUBS_READ Read subscription is allowed
  • *
  • SND_SEQ_PORT_CAP_SUBS_WRITE Write subscription is allowed
  • *
  • SND_SEQ_PORT_CAP_NO_EXPORT Subscription management from 3rd clients is disallowed
  • *
* @param capability The capability bitmap. * @see getCapability() */ void PortInfo::setCapability(unsigned int capability) { snd_seq_port_info_set_capability(m_Info, capability); } /** * Sets the port type. * * The port type is defined combining some of the type bit flags: *
    *
  • SND_SEQ_PORT_TYPE_SPECIFIC Hardware specific port
  • *
  • SND_SEQ_PORT_TYPE_MIDI_GENERIC Generic MIDI device
  • *
  • SND_SEQ_PORT_TYPE_MIDI_GM General MIDI compatible device
  • *
  • SND_SEQ_PORT_TYPE_MIDI_GM2 General MIDI 2 compatible device
  • *
  • SND_SEQ_PORT_TYPE_MIDI_GS GS compatible device
  • *
  • SND_SEQ_PORT_TYPE_MIDI_XG XG compatible device
  • *
  • SND_SEQ_PORT_TYPE_MIDI_MT32 MT-32 compatible device
  • *
  • SND_SEQ_PORT_TYPE_HARDWARE Implemented in hardware
  • *
  • SND_SEQ_PORT_TYPE_SOFTWARE Implemented in software
  • *
  • SND_SEQ_PORT_TYPE_SYNTHESIZER Generates sound
  • *
  • SND_SEQ_PORT_TYPE_PORT Connects to other device(s)
  • *
  • SND_SEQ_PORT_TYPE_APPLICATION Application (sequencer/editor)
  • *
* @param type The port type bitmap * @see getType() */ void PortInfo::setType(unsigned int type) { snd_seq_port_info_set_type(m_Info, type); } /** * Set the MIDI channels * @param channels The MIDI channels * @see getMidiChannels() */ void PortInfo::setMidiChannels(int channels) { snd_seq_port_info_set_midi_channels(m_Info, channels); } /** * Sets the MIDI voices * @param voices The MIDI voices * @see getMidiVoices() */ void PortInfo::setMidiVoices(int voices) { snd_seq_port_info_set_midi_voices(m_Info, voices); } /** * Sets the synth voices * @param voices The synth voices * @see getSynthVoices() */ void PortInfo::setSynthVoices(int voices) { snd_seq_port_info_set_synth_voices(m_Info, voices); } /** * Sets the port-specified mode * @param val The port-specified mode. * @see getPortSpecified() */ void PortInfo::setPortSpecified(int val) { snd_seq_port_info_set_port_specified(m_Info, val); } /** * Gets the list of read subscribers * @return The list of read subscribers */ SubscribersList PortInfo::getReadSubscribers() const { return m_ReadSubscribers; // copy } /** * Gets the list of write subscribers * @return The list of write subscribers */ SubscribersList PortInfo::getWriteSubscribers() const { return m_WriteSubscribers; // copy } /** * Obtains the port subscribers lists * @param seq An opened MidiClient instance */ void PortInfo::readSubscribers(MidiClient* seq) { Subscriber subs; snd_seq_addr_t tmp; freeSubscribers(); tmp.client = getClient(); tmp.port = getPort(); // Read subs subs.setType(SND_SEQ_QUERY_SUBS_READ); subs.setIndex(0); subs.setRoot(&tmp); while (snd_seq_query_port_subscribers(seq->getHandle(), subs.m_Info) >= 0) { m_ReadSubscribers.append(subs); subs.setIndex(subs.getIndex() + 1); } // Write subs subs.setType(SND_SEQ_QUERY_SUBS_WRITE); subs.setIndex(0); subs.setRoot(&tmp); while (snd_seq_query_port_subscribers(seq->getHandle(), subs.m_Info) >= 0) { m_WriteSubscribers.append(subs); subs.setIndex(subs.getIndex() + 1); } } /** * Releases the subscribers lists */ void PortInfo::freeSubscribers() { m_ReadSubscribers.clear(); m_WriteSubscribers.clear(); } /** * Sets the client name. * @see getClientName() * @param name Client name */ void PortInfo::setClientName(QString name) { m_ClientName = name; } /** * Gets the size of the ALSA info object * @return The size of the object */ int PortInfo::getSizeOfInfo() const { return snd_seq_port_info_sizeof(); } /** * Gets the timestamping mode * @return The timestamping mode * @see setTimestamping() */ bool PortInfo::getTimestamping() { return (snd_seq_port_info_get_timestamping(m_Info) == 1); } /** * Gets the timestamping real mode * @return The timestamping real mode * @see setTimestampReal() */ bool PortInfo::getTimestampReal() { return (snd_seq_port_info_get_timestamp_real(m_Info) == 1); } /** * Gets the timestamping queue number * @return The timestamping queue number * @see setTimestampQueue() */ int PortInfo::getTimestampQueue() { return snd_seq_port_info_get_timestamp_queue(m_Info); } /** * Sets the timestamping mode * @param value The timestamping mode * @see getTimestamping() */ void PortInfo::setTimestamping(bool value) { snd_seq_port_info_set_timestamping(m_Info, value?1:0); } /** * Sets the timestamping real mode * @param value The timestamping real mode * @see getTimestampReal() */ void PortInfo::setTimestampReal(bool value) { snd_seq_port_info_set_timestamp_real(m_Info, value?1:0); } /** * Sets the timestamp queue number * @param queueId The timestamp queue number * @see getTimestampQueue() */ void PortInfo::setTimestampQueue(int queueId) { snd_seq_port_info_set_timestamp_queue(m_Info, queueId); } /** * Constructor * @param parent An optional parent object */ MidiPort::MidiPort( QObject* parent ) : QObject( parent ), m_MidiClient( nullptr ), m_Attached( false ) {} /** * Destructor. * * All subscriptions are released. */ MidiPort::~MidiPort() { unsubscribeAll(); detach(); freeSubscriptions(); } /** * Gets the PortInfo object pointer * @return the PortInfo object pointer */ PortInfo* MidiPort::getPortInfo() { return &m_Info; } /** * Gets the list of susbcriptions * @return The list of susbcriptions */ SubscriptionsList MidiPort::getSubscriptions() const { return m_Subscriptions; } /** * Releases the lists of subscriptions. */ void MidiPort::freeSubscriptions() { m_Subscriptions.clear(); } /** * Sets the MidiClient * @param seq A MidiClient object pointer */ void MidiPort::setMidiClient( MidiClient* seq ) { if (m_MidiClient != seq) { m_MidiClient = seq; Q_EMIT midiClientChanged( this, m_MidiClient ); applyPortInfo(); } } /** * Subscribe a Subscription object * @param subs A Subscription object pointer */ void MidiPort::subscribe(Subscription* subs) { subs->subscribe(m_MidiClient); m_Subscriptions.append(*subs); Q_EMIT subscribed(this, subs); } /** * Unsubscribe a Subscription object * @param subs A Subscription object pointer */ void MidiPort::unsubscribe( Subscription* subs ) { Subscription subs2; if (m_MidiClient == nullptr) { return; } subs->unsubscribe(m_MidiClient); SubscriptionsList::iterator it; for(it = m_Subscriptions.begin(); it != m_Subscriptions.end(); ++it) { subs2 = (*it); if ((subs2.getSender()->client == subs->getSender()->client) && (subs2.getSender()->port == subs->getSender()->port) && (subs2.getDest()->client == subs->getDest()->client) && (subs2.getDest()->port == subs->getDest()->port)) { m_Subscriptions.erase(it); break; } } } /** * Subscribe to another port destination * @param info A PortInfo object pointer */ void MidiPort::subscribeTo( PortInfo* info ) { Subscription subs; subs.setSender(m_Info.getAddr()); subs.setDest(info->getAddr()); subscribe(&subs); } /** * Susbcribe to another port destination * @param client ALSA client number * @param port ALSA port number */ void MidiPort::subscribeTo( int client, int port ) { Subscription subs; snd_seq_addr addr; addr.client = client; addr.port = port; subs.setSender(m_Info.getAddr()); subs.setDest(&addr); subscribe(&subs); } /** * Subscribe to another port destination * @param name A string representing a client:port pair */ void MidiPort::subscribeTo( QString const& name ) { Subscription subs; snd_seq_addr addr; if ((m_MidiClient != nullptr) && (m_MidiClient->getHandle() != nullptr)) { subs.setSender(m_Info.getAddr()); if (m_MidiClient->parseAddress(name, addr)) { subs.setDest(&addr); subscribe(&subs); } } } /** * Unsubscribe a destination port * @param name A string representing a client:port pair */ void MidiPort::unsubscribeTo( QString const& name ) { Subscription subs; snd_seq_addr addr; if ((m_MidiClient != nullptr) && (m_MidiClient->getHandle() != nullptr)) { subs.setSender(m_Info.getAddr()); if (m_MidiClient->parseAddress(name, addr)) { subs.setDest(&addr); unsubscribe(&subs); } } } /** * Unsubscribe a destination port * @param port A PortInfo object pointer */ void MidiPort::unsubscribeTo( PortInfo* port ) { Subscription subs; if ((m_MidiClient != nullptr) && (m_MidiClient->getHandle() != nullptr)) { subs.setSender(m_Info.getAddr()); subs.setDest(port->getAddr()); unsubscribe(&subs); } } /** * Unsubscribe a destination port * @param addr An ALSA address record pointer */ void MidiPort::unsubscribeTo( const snd_seq_addr_t* addr ) { Subscription subs; if ((m_MidiClient != nullptr) && (m_MidiClient->getHandle() != nullptr)) { subs.setSender(m_Info.getAddr()); subs.setDest(addr); unsubscribe(&subs); } } /** * Subscribe a source port * @param port A PortInfo object pointer */ void MidiPort::subscribeFrom( PortInfo* port ) { Subscription subs; subs.setSender( port->getAddr() ); subs.setDest( m_Info.getAddr() ); subscribe(&subs); } /** * Subscribe a source port * @param client ALSA client number * @param port ALSA port number */ void MidiPort::subscribeFrom( int client, int port ) { Subscription subs; snd_seq_addr addr; addr.client = client; addr.port = port; subs.setSender(&addr); subs.setDest(m_Info.getAddr()); subscribe(&subs); } /** * Subscribe a source port * @param name A string representing a client:port pair */ void MidiPort::subscribeFrom( QString const& name ) { Subscription subs; snd_seq_addr addr; if ((m_MidiClient != nullptr) && (m_MidiClient->getHandle() != nullptr)) { if (m_MidiClient->parseAddress(name, addr)) { subs.setSender(&addr); subs.setDest(m_Info.getAddr()); subscribe(&subs); } } } /** * Unsubscribe a source port * @param name A string representing a client:port pair */ void MidiPort::unsubscribeFrom( QString const& name ) { Subscription subs; snd_seq_addr addr; if ((m_MidiClient != nullptr) && (m_MidiClient->getHandle() != nullptr)) { if (m_MidiClient->parseAddress(name, addr)) { subs.setSender(&addr); subs.setDest(m_Info.getAddr()); unsubscribe(&subs); } } } /** * Unsubscribe a source port * @param port A PortInfo object pointer */ void MidiPort::unsubscribeFrom( PortInfo* port ) { Subscription subs; if ((m_MidiClient != nullptr) && (m_MidiClient->getHandle() != nullptr)) { subs.setSender(port->getAddr()); subs.setDest(m_Info.getAddr()); unsubscribe(&subs); } } /** * Unsubscribe a source port * @param addr An ALSA address record pointer */ void MidiPort::unsubscribeFrom( const snd_seq_addr_t* addr ) { Subscription subs; if ((m_MidiClient != nullptr) && (m_MidiClient->getHandle() != nullptr)) { subs.setSender(addr); subs.setDest(m_Info.getAddr()); unsubscribe(&subs); } } /** * Subscribe from the System:announce port */ void MidiPort::subscribeFromAnnounce() { subscribeFrom(SND_SEQ_CLIENT_SYSTEM, SND_SEQ_PORT_SYSTEM_ANNOUNCE); } /** * Unsubscribe all subscriptions */ void MidiPort::unsubscribeAll() { if (m_MidiClient == nullptr) { return; } SubscriptionsList::Iterator it; for( it = m_Subscriptions.begin(); it != m_Subscriptions.end(); ++it) { Subscription s = (*it); s.unsubscribe(m_MidiClient); } m_Subscriptions.clear(); } /** * Applies all the the delayed PortInfo changes to the MIDI port object */ void MidiPort::applyPortInfo() { if (m_Attached && (m_MidiClient != nullptr) && (m_MidiClient->isOpened())) { DRUMSTICK_ALSA_CHECK_WARNING(snd_seq_set_port_info( m_MidiClient->getHandle(), m_Info.getPort(), m_Info.m_Info )); } } /** * Gets the port name * @return The port name */ QString MidiPort::getPortName() { return m_Info.getName(); } /** * Sets the port name * @param newName The new port name */ void MidiPort::setPortName( QString const& newName ) { m_Info.setName(newName); applyPortInfo(); } /** * Gets the port number * @return The port number */ int MidiPort::getPortId() { return m_Info.getPort(); } /** * Gets the port capabilities * @return The capabilities bitmap * @see PortInfo::getCapability() */ unsigned int MidiPort::getCapability() { return m_Info.getCapability(); } /** * Sets the port capabilities * @param newValue The capabilities bitmap * @see PortInfo::setCapability() */ void MidiPort::setCapability(unsigned int newValue) { m_Info.setCapability(newValue); applyPortInfo(); } /** * Gets the port type * @return The port type bitmap * @see PortInfo::getType() */ unsigned int MidiPort::getPortType() { return m_Info.getType(); } /** * Sets the port type bitmap * @param newValue The port type flags bitmap * @see PortInfo::setType() */ void MidiPort::setPortType( unsigned int newValue) { m_Info.setType( newValue ); applyPortInfo(); } /** * Gets the MIDI channels * @return The MIDI channels */ int MidiPort::getMidiChannels() { return m_Info.getMidiChannels(); } /** * Sets the MIDI channels * @param newValue The MIDI channels */ void MidiPort::setMidiChannels(int newValue) { m_Info.setMidiChannels( newValue ); applyPortInfo(); } /** * Gets the MIDI voices * @return The MIDI voices */ int MidiPort::getMidiVoices() { return m_Info.getMidiVoices(); } /** * Sets the MIDI voices * @param newValue The MIDI voices */ void MidiPort::setMidiVoices(int newValue) { m_Info.setMidiVoices( newValue ); applyPortInfo(); } /** * Gets the synth voices * @return The synth voices */ int MidiPort::getSynthVoices() { return m_Info.getSynthVoices(); } /** * Sets the synth voices * @param newValue The synth voices */ void MidiPort::setSynthVoices(int newValue) { m_Info.setSynthVoices( newValue ); applyPortInfo(); } /** * Gets the timestamping mode * @return The timestamping mode */ bool MidiPort::getTimestamping() { return m_Info.getTimestamping(); } /** * Gets the timestamp real mode * @return The timestamp real mode */ bool MidiPort::getTimestampReal() { return m_Info.getTimestampReal(); } /** * Gets the timestamp queue number * @return The timestamp queue number */ int MidiPort::getTimestampQueue() { return m_Info.getTimestampQueue(); } /** * Sets the timestamping mode * @param value The timestamping mode */ void MidiPort::setTimestamping(bool value) { m_Info.setTimestamping(value); applyPortInfo(); } /** * Sets the timestamp real mode * @param value The timestamp real mode */ void MidiPort::setTimestampReal(bool value) { m_Info.setTimestampReal(value); applyPortInfo(); } /** * Sets the timestamp queue number * @param queueId The queue number */ void MidiPort::setTimestampQueue(int queueId) { m_Info.setTimestampQueue(queueId); applyPortInfo(); } /** * Attach the port to a MidiClient instance * @param seq A MidiClient object pointer */ void MidiPort::attach( MidiClient* seq ) { if (!m_Attached && (seq != nullptr)) { m_MidiClient = seq; m_MidiClient->portAttach(this); m_Attached = true; Q_EMIT attached(this); } } /** * Detach the port from any MidiClient instance previously attached */ void MidiPort::detach() { if (m_Attached && (m_MidiClient != nullptr)) { m_MidiClient->portDetach(this); m_Attached = false; Q_EMIT detached(this); } } /** * Update the subscribers list in the PortInfo member */ void MidiPort::updateSubscribers() { m_Info.readSubscribers(m_MidiClient); } /** * Gets the list of read subscribers * @return The list of read subscribers */ PortInfoList MidiPort::getReadSubscribers() { const SubscribersList subs(m_Info.getReadSubscribers()); PortInfoList lst; SubscribersList::ConstIterator it; for(it = subs.constBegin(); it != subs.constEnd(); ++it) { Subscriber s = *it; int client = s.getAddr()->client; if ((client != SND_SEQ_CLIENT_SYSTEM) && (client != m_Info.getClient())) { int port = s.getAddr()->port; PortInfo p(m_MidiClient, client, port); if ((p.getCapability() & SND_SEQ_PORT_CAP_NO_EXPORT) == 0) { p.setClientName(m_MidiClient->getClientName(client)); lst << p; } } } return lst; } /** * Gets the list of write subscribers * @return The list of write subscribers */ PortInfoList MidiPort::getWriteSubscribers() { const SubscribersList subs(m_Info.getWriteSubscribers()); PortInfoList lst; SubscribersList::ConstIterator it; for(it = subs.constBegin(); it != subs.constEnd(); ++it) { Subscriber s = *it; int client = s.getAddr()->client; if ((client != SND_SEQ_CLIENT_SYSTEM) && (client != m_Info.getClient())) { int port = s.getAddr()->port; PortInfo p(m_MidiClient, client, port); if ((p.getCapability() & SND_SEQ_PORT_CAP_NO_EXPORT) == 0) { p.setClientName(m_MidiClient->getClientName(client)); lst << p; } } } return lst; } /** * Checks if the provided address is included in the port list * @param addr ALSA address record pointer * @param lst List of port information containers * @return True if the address is found */ bool MidiPort::containsAddress(const snd_seq_addr_t* addr, const PortInfoList& lst) { PortInfoList::ConstIterator i; for( i = lst.begin(); i != lst.end(); ++i) { PortInfo p = *i; if ((p.getAddr()->client == addr->client) && (p.getAddr()->port == addr->port)) { return true; } } return false; } /** * Update the write subscriptions * @param ports List of writable ports to be subscribed */ void MidiPort::updateConnectionsTo(const PortInfoList& ports) { PortInfoList subs(getReadSubscribers()); PortInfoList::ConstIterator i; for (i = subs.constBegin(); i != subs.constEnd(); ++i) { PortInfo s = *i; if (!containsAddress(s.getAddr(), ports)) { unsubscribeTo(s.getAddr()); } } for (i = ports.constBegin(); i != ports.constEnd(); ++i) { PortInfo p = *i; if (!containsAddress(p.getAddr(), subs)) { subscribeTo(&p); } } } /** * Update the read susbcriptions * @param ports List of readable ports to be subscribed */ void MidiPort::updateConnectionsFrom(const PortInfoList& ports) { PortInfoList subs(getWriteSubscribers()); PortInfoList::ConstIterator i; for (i = subs.constBegin(); i != subs.constEnd(); ++i) { PortInfo s = *i; if (!containsAddress(s.getAddr(), ports)) { unsubscribeFrom(s.getAddr()); } } for (i = ports.constBegin(); i != ports.constEnd(); ++i) { PortInfo p = *i; if (!containsAddress(p.getAddr(), subs)) { subscribeFrom(&p); } } } } // namespace ALSA } // namespace drumstick drumstick-2.9.0/library/alsa/alsatimer.cpp0000644000175000017500000007350414541630232017615 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "errorcheck.h" #include #include #include #include #include /** * @file alsatimer.cpp * Implementation of classes managing ALSA Timers */ namespace drumstick { namespace ALSA { /** * @addtogroup ALSATimer * @{ * * Timers provide periodic time events to applications, and also to the ALSA * sequencer. * * There are two mechanisms to deliver the timer events. To use the callback * mechanism, a class must be derived from TimerEventHandler, and a instance * of the derived class must be assigned to the Timer instance using * Timer::setHandler(). If the handler is not assigned, then the Timer instance * will generate the signal Timer::timerExpired(). * * Classes: * * TimerInfo: ALSA Timer information container. * * This class is used to hold properties about ALSA Timers. * * TimerId: ALSA Timer identifier container. * * This class provides an unique identifier for a Timer. * * TimerGlobalInfo: Global timer information container. * * This class provides global timer parameters. * * TimerQuery: ALSA Timer inquiry helper. * * This class provides a mechanism to enumerate the available system timers. * * TimerParams: ALSA Timer parameters container. * * This class provides several parameters about a Timer. * * TimerStatus: ALSA Timer status container. * * This class provides some status information about a Timer. * * TimerEventHandler: ALSA Timer events handler. * * This abstract class is used to define an interface that other class can * implement to receive timer events. * * Timer: ALSA Timer management. * * This class represents an ALSA timer object. * * @see https://www.alsa-project.org/alsa-doc/alsa-lib/group___timer.html * @} */ /** * Constructor */ TimerInfo::TimerInfo() { snd_timer_info_malloc(&m_Info); } /** * Cosntructor * @param other ALSA timer info object pointer */ TimerInfo::TimerInfo(const snd_timer_info_t *other) { snd_timer_info_malloc(&m_Info); snd_timer_info_copy(m_Info, other); } /** * Copy constructor * @param other Existing TimerInfo object reference */ TimerInfo::TimerInfo(const TimerInfo& other) { snd_timer_info_malloc(&m_Info); snd_timer_info_copy(m_Info, other.m_Info); } /** * Destructor */ TimerInfo::~TimerInfo() { snd_timer_info_free(m_Info); } /** * Copy the current object * @return Pointer to the new object */ TimerInfo* TimerInfo::clone() { return new TimerInfo(m_Info); } /** * Assignment operator * @param other Existing TimerInfo object reference * @return a reference of this object */ TimerInfo& TimerInfo::operator=(const TimerInfo& other) { if (this == &other) return *this; snd_timer_info_copy(m_Info, other.m_Info); return *this; } /** * Check if the timer is slave (depends on another device) * @return True if the timer is slave */ bool TimerInfo::isSlave() { return (snd_timer_info_is_slave(m_Info) != 0); } /** * Gets the card number * @return Card number */ int TimerInfo::getCard() { return snd_timer_info_get_card(m_Info); } /** * Gets the string identifier * @return String identifier */ QString TimerInfo::getId() { return QString(snd_timer_info_get_id(m_Info)); } /** * Gets the timer name * @return Timer name */ QString TimerInfo::getName() { return QString(snd_timer_info_get_name(m_Info)); } /** * Gets the timer resolution (timer period in nanoseconds) * @return Timer resolution in nanos */ long TimerInfo::getResolution() { return snd_timer_info_get_resolution(m_Info); } /** * Gets the timer frequency in Hz * @return Timer frequency in Hz */ long TimerInfo::getFrequency() { long res = getResolution(); if (res > 0) { return 1000000000L / res; } return 0; } /** * Gets the size of the ALSA timer info object * @return Size of the ALSA object */ int TimerInfo::getSizeOfInfo() const { return snd_timer_info_sizeof(); } /** * Gets the maximum timer ticks * @return Maximum timer ticks * @deprecated */ long TimerInfo::getTicks() { return snd_timer_info_get_ticks(m_Info); } /** * Constructor */ TimerId::TimerId() { snd_timer_id_malloc(&m_Info); } /** * Constructor * @param other ALSA timer ID object pointer */ TimerId::TimerId(const snd_timer_id_t *other) { snd_timer_id_malloc(&m_Info); snd_timer_id_copy(m_Info, other); if (getCard() < 0) setCard(0); if (getDevice() < 0) setDevice(0); if (getSubdevice() < 0) setSubdevice(0); } /** * Copy constructor * @param other Existing TimerId object reference */ TimerId::TimerId(const TimerId& other) { snd_timer_id_malloc(&m_Info); snd_timer_id_copy(m_Info, other.m_Info); if (getCard() < 0) setCard(0); if (getDevice() < 0) setDevice(0); if (getSubdevice() < 0) setSubdevice(0); } /** * Constructor * @param cls Class * @param scls Subclass * @param card Card * @param dev Device * @param sdev Subdevice */ TimerId::TimerId(int cls, int scls, int card, int dev, int sdev) { snd_timer_id_malloc(&m_Info); setClass(cls); setSlaveClass(scls); setCard(card); setDevice(dev); setSubdevice(sdev); } /** * Destructor */ TimerId::~TimerId() { snd_timer_id_free(m_Info); } /** * Copy the object * @return Pointer to the new object */ TimerId* TimerId::clone() { return new TimerId(m_Info); } /** * Assignment operator * @param other Existing TimerId object reference * @return This object */ TimerId& TimerId::operator=(const TimerId& other) { if (this == &other) return *this; snd_timer_id_copy(m_Info, other.m_Info); if (getCard() < 0) setCard(0); if (getDevice() < 0) setDevice(0); if (getSubdevice() < 0) setSubdevice(0); return *this; } /** * Set the class identifier. Existing classes: *
    *
  • SND_TIMER_CLASS_SLAVE: slave timer
  • *
  • SND_TIMER_CLASS_GLOBAL: global timer
  • *
  • SND_TIMER_CLASS_CARD: card timer
  • *
  • SND_TIMER_CLASS_PCM: PCM timer
  • *
* @param devclass Class identifier. */ void TimerId::setClass(int devclass) { snd_timer_id_set_class(m_Info, devclass); } /** * Gets the class identifier. * @return Class identifier * @see setClass() */ int TimerId::getClass() { return snd_timer_id_get_class(m_Info); } /** * Sets the Slave class * @param devsclass Slave class */ void TimerId::setSlaveClass(int devsclass) { snd_timer_id_set_sclass(m_Info, devsclass); } /** * Gets the slave class * @return Slave class */ int TimerId::getSlaveClass() { return snd_timer_id_get_sclass(m_Info); } /** * Sets the card number * @param card Card number */ void TimerId::setCard(int card) { snd_timer_id_set_card(m_Info, card); } /** * Gets the card number * @return Card number */ int TimerId::getCard() { return snd_timer_id_get_card(m_Info); } /** * Sets the device number * @param device Device number */ void TimerId::setDevice(int device) { snd_timer_id_set_device(m_Info, device); } /** * Gets the device number * @return Device number */ int TimerId::getDevice() { return snd_timer_id_get_device(m_Info); } /** * Sets the subdevice number * @param subdevice Subdevice number */ void TimerId::setSubdevice(int subdevice) { snd_timer_id_set_subdevice (m_Info, subdevice); } /** * Gets the subdevice number * @return Subdevice number */ int TimerId::getSubdevice() { return snd_timer_id_get_subdevice(m_Info); } /** * Gets the size of the ALSA timer ID object * @return Size of the ALSA object */ int TimerId::getSizeOfInfo() const { return snd_timer_id_sizeof(); } /** * Constructor * @param deviceName Device name, usually "hw" * @param openMode Open mode (unknown values) */ TimerQuery::TimerQuery(const QString& deviceName, int openMode) { DRUMSTICK_ALSA_CHECK_WARNING( snd_timer_query_open( &m_Info, deviceName.toLocal8Bit().data(), openMode )); readTimers(); } /** * Constructor * @param deviceName Device name, usually "hw" * @param openMode Open mode (unknown values) * @param conf ALSA configuration object pointer */ TimerQuery::TimerQuery( const QString& deviceName, int openMode, snd_config_t* conf ) { DRUMSTICK_ALSA_CHECK_WARNING( snd_timer_query_open_lconf( &m_Info, deviceName.toLocal8Bit().data(), openMode, conf )); readTimers(); } /** * Destructor */ TimerQuery::~TimerQuery() { freeTimers(); snd_timer_query_close(m_Info); } /** * Enumerate the available timers storing the results into an internal list */ void TimerQuery::readTimers() { TimerId tid; snd_timer_id_set_class(tid.m_Info, SND_TIMER_CLASS_NONE); for(;;) { int rc = snd_timer_query_next_device(m_Info, tid.m_Info); if ((rc < 0) || (tid.getClass() < 0)) { break; } m_timers.append(tid); } } /** * Release the internal list of timers */ void TimerQuery::freeTimers() { m_timers.clear(); } /** * Get a TimerGlobalInfo object * @return TimerGlobalInfo object reference */ TimerGlobalInfo& TimerQuery::getGlobalInfo() { snd_timer_query_info(m_Info, m_GlobalInfo.m_Info); return m_GlobalInfo; } /** * Sets the global parameters * @param params Pointer to an ALSA timer global parameters object */ void TimerQuery::setGlobalParams(snd_timer_gparams_t* params) { snd_timer_query_params(m_Info, params); } /** * Gets the global timer parameters * @param params Pointer to an ALSA timer global parameters object */ void TimerQuery::getGlobalParams(snd_timer_gparams_t* params) { snd_timer_query_params(m_Info, params); } /** * Gets the global timer status * @param status Pointer to an ALSA timer global status object */ void TimerQuery::getGlobalStatus(snd_timer_gstatus_t *status) { snd_timer_query_status(m_Info, status); } /** * Default constructor */ TimerGlobalInfo::TimerGlobalInfo() { snd_timer_ginfo_malloc(&m_Info); } /** * Constructor * @param other ALSA global info object pointer */ TimerGlobalInfo::TimerGlobalInfo(const snd_timer_ginfo_t* other) { snd_timer_ginfo_malloc(&m_Info); snd_timer_ginfo_copy(m_Info, other); } /** * Copy constructor * @param other Existing TimerGlobalInfo object reference */ TimerGlobalInfo::TimerGlobalInfo(const TimerGlobalInfo& other) { snd_timer_ginfo_malloc(&m_Info); snd_timer_ginfo_copy(m_Info, other.m_Info); } /** * Destructor */ TimerGlobalInfo::~TimerGlobalInfo() { snd_timer_ginfo_free(m_Info); } /** * Copy the current object * @return Pointer to the new object */ TimerGlobalInfo* TimerGlobalInfo::clone() { return new TimerGlobalInfo(m_Info); } /** * Assignment operator * @param other Existing TimerGlobalInfo object reference * @return This object */ TimerGlobalInfo& TimerGlobalInfo::operator=(const TimerGlobalInfo& other) { if (this == &other) return *this; snd_timer_ginfo_copy(m_Info, other.m_Info); return *this; } /** * Sets the timer identifier * @param tid TimerId object reference */ void TimerGlobalInfo::setTimerId(const TimerId& tid) { m_Id = tid; snd_timer_ginfo_set_tid (m_Info, m_Id.m_Info); } /** * Gets the timer identifier * @return TimerId object reference */ TimerId& TimerGlobalInfo::getTimerId() { m_Id = TimerId(snd_timer_ginfo_get_tid (m_Info)); return m_Id; } /** * Gets the flags * @return Undocumented flags */ unsigned int TimerGlobalInfo::getFlags() { return snd_timer_ginfo_get_flags (m_Info); } /** * Gets the card number * @return Card number */ int TimerGlobalInfo::getCard() { return snd_timer_ginfo_get_card (m_Info); } /** * Gets the timer ID string * @return Timer ID string */ QString TimerGlobalInfo::getId() { return QString(snd_timer_ginfo_get_id (m_Info)); } /** * Gets the timer name * @return Timer name */ QString TimerGlobalInfo::getName() { return QString(snd_timer_ginfo_get_name (m_Info)); } /** * Gets the timer resolution in ns * @return Timer resolution in ns */ unsigned long TimerGlobalInfo::getResolution() { return snd_timer_ginfo_get_resolution (m_Info); } /** * Gets timer minimal resolution in ns * @return Minimal resolution in ns */ unsigned long TimerGlobalInfo::getMinResolution() { return snd_timer_ginfo_get_resolution_min (m_Info); } /** * Gets timer maximal resolution in ns * @return Maximal resolution in ns */ unsigned long TimerGlobalInfo::getMaxResolution() { return snd_timer_ginfo_get_resolution_max(m_Info); } /** * Gets current timer clients * @return Current clients */ unsigned int TimerGlobalInfo::getClients() { return snd_timer_ginfo_get_clients(m_Info); } /** * Gets the size of the ALSA timer global info object * @return Size of the ALSA object */ int TimerGlobalInfo::getSizeOfInfo() const { return snd_timer_ginfo_sizeof(); } /** * Default constructor */ TimerParams::TimerParams() { snd_timer_params_malloc (&m_Info); } /** * Constructor * @param other Pointer to an ALSA timer parameters object */ TimerParams::TimerParams(const snd_timer_params_t *other) { snd_timer_params_malloc (&m_Info); snd_timer_params_copy (m_Info, other); } /** * Copy constructor * @param other Existing TimerParams object reference */ TimerParams::TimerParams(const TimerParams& other) { snd_timer_params_malloc (&m_Info); snd_timer_params_copy (m_Info, other.m_Info); } /** * Destructor */ TimerParams::~TimerParams() { snd_timer_params_free (m_Info); } /** * Copy the current object * @return Pointer to the new object */ TimerParams* TimerParams::clone() { return new TimerParams(m_Info); } /** * Assignment operator * @param other Existing TimerParams object reference * @return This object */ TimerParams& TimerParams::operator=(const TimerParams& other) { if (this == &other) return *this; snd_timer_params_copy (m_Info, other.m_Info); return *this; } /** * Sets the automatic start flag * @param auto_start Value for the automatic start flag */ void TimerParams::setAutoStart(bool auto_start) { snd_timer_params_set_auto_start (m_Info, auto_start ? 1 : 0); } /** * Gets the automatic start flag * @return True if the timer starts automatically */ bool TimerParams::getAutoStart() { return (snd_timer_params_get_auto_start (m_Info) != 0); } /** * Sets the exclusive flag * @param exclusive True if the timer has the exclusive flag */ void TimerParams::setExclusive(bool exclusive) { snd_timer_params_set_exclusive (m_Info, exclusive ? 1 : 0); } /** * Gets the timer's exclusive flag * @return True if the timer has the exclusive flag */ bool TimerParams::getExclusive() { return (snd_timer_params_get_exclusive (m_Info) != 0); } /** * Sets the timer early event * @param early_event Timer early event */ void TimerParams::setEarlyEvent(bool early_event) { snd_timer_params_set_early_event (m_Info, early_event ? 1 : 0); } /** * Gets the timer early event * @return Timer early event */ bool TimerParams::getEarlyEvent() { return (snd_timer_params_get_early_event (m_Info) != 0); } /** * Sets the timer ticks * @param ticks Timer ticks */ void TimerParams::setTicks(long ticks) { snd_timer_params_set_ticks (m_Info, ticks); } /** * Gets the timer ticks * @return Timer ticks */ long TimerParams::getTicks() { return snd_timer_params_get_ticks (m_Info); } /** * Sets the queue size (32-1024) * @param queue_size Queue size */ void TimerParams::setQueueSize(long queue_size) { snd_timer_params_set_queue_size (m_Info, queue_size); } /** * Gets the queue size * @return Queue size */ long TimerParams::getQueueSize() { return snd_timer_params_get_queue_size (m_Info); } /** * Sets the event filter * @param filter Event filter */ void TimerParams::setFilter(unsigned int filter) { snd_timer_params_set_filter (m_Info, filter); } /** * Gets the event filter * @return Event filter */ unsigned int TimerParams::getFilter() { return snd_timer_params_get_filter (m_Info); } /** * Gets the size of the ALSA timer parameters object * @return Size of the ALSA object */ int TimerParams::getSizeOfInfo() const { return snd_timer_params_sizeof(); } /** * Default constructor */ TimerStatus::TimerStatus() { snd_timer_status_malloc (&m_Info); } /** * Constructor * @param other Pointer to an existing ALSA timer status object */ TimerStatus::TimerStatus(const snd_timer_status_t *other) { snd_timer_status_malloc (&m_Info); snd_timer_status_copy (m_Info, other); } /** * Copy constructor * @param other Existing TimerStatus object reference */ TimerStatus::TimerStatus(const TimerStatus& other) { snd_timer_status_malloc (&m_Info); snd_timer_status_copy (m_Info, other.m_Info); } /** * Destructor */ TimerStatus::~TimerStatus() { snd_timer_status_free (m_Info); } /** * Copy the current object * @return Pointer to the new object */ TimerStatus* TimerStatus::clone() { return new TimerStatus(m_Info); } /** * Assignment operator * @param other Existing TimerStatus object reference * @return This object */ TimerStatus& TimerStatus::operator=(const TimerStatus& other) { if (this == &other) return *this; snd_timer_status_copy (m_Info, other.m_Info); return *this; } /** * Gets the high resolution time-stamp * @return High resolution time-stamp */ snd_htimestamp_t TimerStatus::getTimestamp() { return snd_timer_status_get_timestamp (m_Info); } /** * Gets the resolution in us * @return Resolution in us */ long TimerStatus::getResolution() { return snd_timer_status_get_resolution (m_Info); } /** * Gets the master tick lost count * @return Master tick lost count */ long TimerStatus::getLost() { return snd_timer_status_get_lost (m_Info); } /** * Gets the overrun count * @return Overrun count */ long TimerStatus::getOverrun() { return snd_timer_status_get_overrun (m_Info); } /** * Gets the count of used queue elements * @return Count of used queue elements */ long TimerStatus::getQueue() { return snd_timer_status_get_queue (m_Info); } /** * Gets the size of the ALSA timer status object * @return Size of the ALSA object */ int TimerStatus::getSizeOfInfo() const { return snd_timer_status_sizeof(); } /** * Constructor. * Open flags can be a combination of the following constants: *
    *
  • SND_TIMER_OPEN_NONBLOCK: non-blocking behavior
  • *
  • SND_TIMER_OPEN_TREAD: enhanced read, use time-stamps and event notification
  • *
* @param deviceName Name of the device * @param openMode Open mode flags bitmap * @param parent Optional parent object */ Timer::Timer( const QString& deviceName, int openMode, QObject* parent ) : QObject(parent), m_asyncHandler(nullptr), m_handler(nullptr), m_thread(nullptr), m_deviceName(deviceName) { DRUMSTICK_ALSA_CHECK_ERROR( snd_timer_open( &m_Info, m_deviceName.toLocal8Bit().data(), openMode )); } /** * Constructor. * Open flags can be a combination of the following constants: *
    *
  • SND_TIMER_OPEN_NONBLOCK: non-blocking behavior
  • *
  • SND_TIMER_OPEN_TREAD: enhanced read, use time-stamps and event notification
  • *
* @param deviceName Name of the device * @param openMode Open mode flags bitmap * @param conf ALSA configuration object pointer * @param parent Optional parent object */ Timer::Timer( const QString& deviceName, int openMode, snd_config_t* conf, QObject* parent ) : QObject(parent), m_asyncHandler(nullptr), m_handler(nullptr), m_thread(nullptr), m_deviceName(deviceName) { DRUMSTICK_ALSA_CHECK_ERROR( snd_timer_open_lconf( &m_Info, m_deviceName.toLocal8Bit().data(), openMode, conf )); } /** * Constructor * Open flags can be a combination of the following constants: *
    *
  • SND_TIMER_OPEN_NONBLOCK: non-blocking behavior
  • *
  • SND_TIMER_OPEN_TREAD: enhanced read, use time-stamps and event notification
  • *
* @param id TimerId object reference * @param openMode Open mode flags bitmap * @param parent Optional parent object */ Timer::Timer( TimerId& id, int openMode, QObject* parent ) : QObject(parent), m_asyncHandler(nullptr), m_handler(nullptr), m_thread(nullptr) { m_deviceName = QString("hw:CLASS=%1,SCLASS=%2,CARD=%3,DEV=%4,SUBDEV=%5") .arg(id.getClass()) .arg(id.getSlaveClass()) .arg(id.getCard()) .arg(id.getDevice()) .arg(id.getSubdevice()); DRUMSTICK_ALSA_CHECK_ERROR( snd_timer_open( &m_Info, m_deviceName.toLocal8Bit().data(), openMode )); } /** * Constructor. * Open flags can be a combination of the following constants: *
    *
  • SND_TIMER_OPEN_NONBLOCK: non-blocking behavior
  • *
  • SND_TIMER_OPEN_TREAD: enhanced read, use time-stamps and event notification
  • *
* @param cls Class * @param scls Subclass * @param card Card * @param dev Device * @param sdev Subdevice * @param openMode Open mode flags bitmap * @param parent Optional parent object */ Timer::Timer( int cls, int scls, int card, int dev, int sdev, int openMode, QObject* parent ) : QObject(parent), m_asyncHandler(nullptr), m_handler(nullptr), m_thread(nullptr) { m_deviceName = QString("hw:CLASS=%1,SCLASS=%2,CARD=%3,DEV=%4,SUBDEV=%5") .arg(cls) .arg(scls) .arg(card) .arg(dev) .arg(sdev); DRUMSTICK_ALSA_CHECK_ERROR( snd_timer_open( &m_Info, m_deviceName.toLocal8Bit().data(), openMode )); } /** * Destructor. */ Timer::~Timer() { stopEvents(); if (m_thread != nullptr) delete m_thread; DRUMSTICK_ALSA_CHECK_WARNING(snd_timer_close(m_Info)); } /** * Adds an asynchronous timer handler function. * @param callback Function handler * @param private_data Any data that will be passed to the callback */ void Timer::addAsyncTimerHandler(snd_async_callback_t callback, void *private_data) { DRUMSTICK_ALSA_CHECK_WARNING(snd_async_add_timer_handler(&m_asyncHandler, m_Info, callback, private_data)); } /** * Gets the ALSA timer handle * @return ALSA timer handle */ snd_timer_t* Timer::getTimerHandle() { return snd_async_handler_get_timer(m_asyncHandler); } /** * Gets the count of poll descriptors * @return Count of poll descriptors */ int Timer::getPollDescriptorsCount() { return snd_timer_poll_descriptors_count(m_Info); } /** * Gets poll descriptors * @param pfds Pointer to a pollfd array * @param space Number of pollfd elements available */ void Timer::pollDescriptors(struct pollfd *pfds, unsigned int space) { DRUMSTICK_ALSA_CHECK_WARNING(snd_timer_poll_descriptors(m_Info, pfds, space)); } /** * Gets returned events from poll descriptors * @param pfds Pointer to a pollfd array * @param nfds Number of pollfd elements available * @param revents Returned events */ void Timer::pollDescriptorsRevents(struct pollfd *pfds, unsigned int nfds, unsigned short *revents) { DRUMSTICK_ALSA_CHECK_WARNING(snd_timer_poll_descriptors_revents(m_Info, pfds, nfds, revents)); } /** * Gets the timer info object * @return TimerInfo object reference */ TimerInfo& Timer::getTimerInfo() { snd_timer_info (m_Info, m_TimerInfo.m_Info); return m_TimerInfo; } /** * Sets the timer parameters * @param params TimerParams object reference */ void Timer::setTimerParams(const TimerParams& params) { DRUMSTICK_ALSA_CHECK_WARNING( snd_timer_params(m_Info, params.m_Info) ); } /** * Gets the timer status * @return TimerStatus object reference */ TimerStatus& Timer::getTimerStatus() { DRUMSTICK_ALSA_CHECK_WARNING( snd_timer_status(m_Info, m_TimerStatus.m_Info) ); return m_TimerStatus; } /** * Start rolling the timer */ void Timer::start() { DRUMSTICK_ALSA_CHECK_WARNING(snd_timer_start(m_Info)); } /** * Stop rolling the timer */ void Timer::stop() { DRUMSTICK_ALSA_CHECK_WARNING(snd_timer_stop(m_Info)); } /** * Continue rolling the timer */ void Timer::continueRunning() { DRUMSTICK_ALSA_CHECK_WARNING(snd_timer_continue(m_Info)); } /** * Read bytes from the timer handle * @param buffer Buffer to store the input bytes * @param size Input buffer size in bytes * @return Bytes read from the timer */ ssize_t Timer::read(void *buffer, size_t size) { return snd_timer_read(m_Info, buffer, size); } /** * Internal function to deliver the timer events using one of the two available * methods: *
    *
  • TimerEventHandler instance pointer provided in Timer::setHandler()
  • *
  • A signal Timer::timerExpired() is emitted, otherwise
  • *
*/ void Timer::doEvents() { snd_timer_tread_t tr; while ( read(&tr, sizeof(tr)) == sizeof(tr) ) { int msecs = ((tr.tstamp.tv_sec - m_last_time.tv_sec) * 1000) + round((tr.tstamp.tv_nsec - m_last_time.tv_nsec) / 1000000.0); m_last_time = tr.tstamp; if ( m_handler != nullptr ) m_handler->handleTimerEvent(tr.val, msecs); else Q_EMIT timerExpired(tr.val, msecs); } } /** * Starts the events dispatching thread */ void Timer::startEvents() { m_last_time = getTimerStatus().getTimestamp(); if (m_thread == nullptr) { m_thread = new TimerInputThread(this, 500); m_thread->start(); } } /** * Stops the events dispatching thread */ void Timer::stopEvents() { int counter = 0; if (m_thread != nullptr) { m_thread->stop(); while (!m_thread->wait(500) && (counter < 10)) { counter++; } if (!m_thread->isFinished()) { m_thread->terminate(); } delete m_thread; } } /** * Check and return the best available global TimerId in the system, meaning * the timer with higher frequency (or lesser period, resolution). * @return A TimerId object * @since 0.3.0 */ TimerId Timer::bestGlobalTimerId() { TimerId id; snd_timer_t* timer; snd_timer_info_t* info; long res, best_res = LONG_MAX; char timername[64]; int test_devs[] = { SND_TIMER_GLOBAL_SYSTEM , SND_TIMER_GLOBAL_RTC #ifdef SND_TIMER_GLOBAL_HPET , SND_TIMER_GLOBAL_HPET #endif #ifdef SND_TIMER_GLOBAL_HRTIMER , SND_TIMER_GLOBAL_HRTIMER #endif }; int max_global_timers = sizeof(test_devs)/sizeof(int); int clas = SND_TIMER_CLASS_GLOBAL; int scls = SND_TIMER_SCLASS_NONE; int card = 0; int dev = SND_TIMER_GLOBAL_SYSTEM; int sdev = 0; int err = 0; int is_slave = 0; int i; snd_timer_info_alloca(&info); // default system timer id.setClass(clas); id.setSlaveClass(scls); id.setCard(card); id.setDevice(dev); id.setSubdevice(sdev); // select a non slave timer with the lowest resolution value for( i = 0; i < max_global_timers; ++i ) { dev = test_devs[i]; sprintf( timername, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i", clas, scls, card, dev, sdev ); err = snd_timer_open(&timer, timername, SND_TIMER_OPEN_NONBLOCK); if (err < 0) continue; err = snd_timer_info(timer, info); if (err == 0) { is_slave = snd_timer_info_is_slave(info); res = snd_timer_info_get_resolution(info); if ((is_slave == 0) && (best_res > res)) { best_res = res; id.setDevice(dev); } } snd_timer_close(timer); } return id; } /** * Check and return the best available global Timer in the system, meaning * the timer with higher frequency (or lesser period, resolution). * @param openMode Open mode flags * @param parent Optional parent object * @return A new Timer instance pointer */ Timer* Timer::bestGlobalTimer(int openMode, QObject* parent) { TimerId id = bestGlobalTimerId(); return new Timer(id, openMode, parent); } /** * Loop reading and dispatching timer events. */ void Timer::TimerInputThread::run() { int err, count; struct pollfd *fds; if (m_timer == nullptr) return; count = m_timer->getPollDescriptorsCount(); fds = (pollfd *) calloc(count, sizeof(struct pollfd)); if (fds == nullptr) { qWarning() << "allocation error!"; return; } fds->events = POLLIN; fds->revents = 0; try { while (!stopped() && (m_timer != nullptr)) { m_timer->pollDescriptors(fds, count); if ((err = poll(fds, count, m_Wait)) < 0) { qWarning() << "poll error " << err << "(" << strerror(err) << ")"; free(fds); return; } if (err == 0) { qWarning() << "timer time out"; free(fds); return; } m_timer->doEvents(); } } catch (...) { qWarning() << "exception in input thread"; } free(fds); } /** * Returns the rolling state of the timer thread * @return The stopped state */ bool Timer::TimerInputThread::stopped() { QReadLocker locker(&m_mutex); return m_Stopped; } /** * Stop the thread */ void Timer::TimerInputThread::stop() { QWriteLocker locker(&m_mutex); m_Stopped = true; } } // namespace ALSA } // namespace drumstick drumstick-2.9.0/library/alsa/playthread.cpp0000644000175000017500000001406014541630232017761 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ extern "C" { #include } #include #include #include #include #include /** * @file playthread.cpp * Implementation of a sequencer output thread */ /** * @addtogroup PlayThread * @{ * ALSA Sequencer easy playback functionality. * SequencerOutputThread provides MIDI sequence playback. * * This is an abstract class that must be extended providing * an implementation for the pure virtual methods: * SequencerOutputThread::hasNext() and SequencerOutputThread::nextEvent() * before using it for MIDI sequence playback. You can use any structure or * class you prefer to store the SequencerEvent objects, but they must be * provided ordered by time. A simplistic Song definition may be: * * @code * typedef QList Song; * @endcode * * Using this class is optional. You may prefer another mechanism to * manage playback actions. This class uses a thread to manage the * sequence playback as a background task. * @} */ namespace drumstick { namespace ALSA { const int TIMEOUT = 100; /** * Constructor * @param seq Existing MidiClient object pointer * @param portId Numeric input/output port identifier */ SequencerOutputThread::SequencerOutputThread(MidiClient *seq, int portId) : QThread(), m_MidiClient(seq), m_Queue(nullptr), m_PortId(portId), m_Stopped(false), m_QueueId(0), m_npfds(0), m_pfds(nullptr) { if (m_MidiClient != nullptr) { m_Queue = m_MidiClient->getQueue(); m_QueueId = m_Queue->getId(); } } /** * Checks if stop has been requested * @return True if stop has been requested * @since 0.2.0 */ bool SequencerOutputThread::stopRequested() { QReadLocker locker(&m_mutex); return m_Stopped; } /** * Stops the playback task */ void SequencerOutputThread::stop() { QWriteLocker locker(&m_mutex); m_Stopped = true; locker.unlock(); while (isRunning()) { wait(TIMEOUT); } } /** * Sends an echo event, with the same PortId as sender and destination. * @param tick Event schedule time in ticks. */ void SequencerOutputThread::sendEchoEvent(int tick) { if (!stopRequested() && m_MidiClient != nullptr) { SystemEvent ev(SND_SEQ_EVENT_ECHO); ev.setSource(m_PortId); ev.setDestination(m_MidiClient->getClientId(), m_PortId); ev.scheduleTick(m_QueueId, tick, false); sendSongEvent(&ev); } } /** * Sends a SequencerEvent * @param ev SequencerEvent object pointer */ void SequencerOutputThread::sendSongEvent(SequencerEvent* ev) { if (m_MidiClient != nullptr) { while (!stopRequested() && (snd_seq_event_output_direct(m_MidiClient->getHandle(), ev->getHandle()) < 0)) { poll(m_pfds, m_npfds, TIMEOUT); } } } /** * Flush the ALSA output buffer. */ void SequencerOutputThread::drainOutput() { if (!stopRequested() && m_MidiClient != nullptr) { while (!stopRequested() && (snd_seq_drain_output(m_MidiClient->getHandle()) < 0)) { poll(m_pfds, m_npfds, TIMEOUT); } } } /** * Waits until the ALSA output queue is empty (all the events have been played.) */ void SequencerOutputThread::syncOutput() { if (!stopRequested() && m_MidiClient != nullptr) { m_MidiClient->synchronizeOutput(); } } /** * Thread process loop */ void SequencerOutputThread::run() { if (m_MidiClient != nullptr) { try { unsigned int last_tick; m_npfds = snd_seq_poll_descriptors_count(m_MidiClient->getHandle(), POLLOUT); m_pfds = (pollfd*) calloc(m_npfds, sizeof(pollfd)); snd_seq_poll_descriptors(m_MidiClient->getHandle(), m_pfds, m_npfds, POLLOUT); last_tick = getInitialPosition(); if (last_tick == 0) { m_Queue->start(); } else { m_Queue->setTickPosition(last_tick); m_Queue->continueRunning(); } while (!stopRequested() && hasNext()) { SequencerEvent* ev = nextEvent(); if (!stopRequested() && !SequencerEvent::isConnectionChange(ev)) { sendSongEvent(ev); } if (getEchoResolution() > 0) { while (!stopRequested() && (last_tick < ev->getTick())) { last_tick += getEchoResolution(); sendEchoEvent(last_tick); } } } if (stopRequested()) { m_Queue->clear(); Q_EMIT playbackStopped(); } else { drainOutput(); syncOutput(); if (stopRequested()) Q_EMIT playbackStopped(); else Q_EMIT playbackFinished(); } m_Queue->stop(); } catch (...) { qWarning("exception in output thread"); } m_npfds = 0; free(m_pfds); m_pfds = nullptr; } } /** * Starts the playback thread * @param priority Thread priority, default is InheritPriority */ void SequencerOutputThread::start( Priority priority ) { QWriteLocker locker(&m_mutex); m_Stopped = false; QThread::start( priority ); } } // namespace ALSA } // namespace drumstick drumstick-2.9.0/library/file/0000755000175000017500000000000014541630232015116 5ustar pedropedrodrumstick-2.9.0/library/file/drumstick-file-config.cmake0000644000175000017500000000014314541630232022303 0ustar pedropedro#include(CMakeFindDependencyMacro) include(${CMAKE_CURRENT_LIST_DIR}/drumstick-file-targets.cmake) drumstick-2.9.0/library/file/file.pro0000644000175000017500000000221314541630232016555 0ustar pedropedroTEMPLATE = lib TARGET = drumstick-file DESTDIR = ../../build/lib DEPENDPATH += . ../include INCLUDEPATH += . ../include include (../../global.pri) CONFIG += c++11 qt create_pc create_prl no_install_prl DEFINES += drumstick_file_EXPORTS QMAKE_CXXFLAGS += $$QMAKE_CXXFLAGS_HIDESYMS QMAKE_PKGCONFIG_PREFIX = $$INSTALLBASE QT -= gui equals(QT_MAJOR_VERSION, 6) { QT += core5compat } # Input HEADERS += ../include/drumstick/macros.h \ ../include/drumstick/rmid.h \ ../include/drumstick/qsmf.h \ ../include/drumstick/qwrk.h SOURCES += rmid.cpp \ qsmf.cpp \ qwrk.cpp static { CONFIG += staticlib DEFINES += DRUMSTICK_STATIC } macx:!static { TARGET = drumstick-file CONFIG += lib_bundle FRAMEWORK_HEADERS.version = Versions FRAMEWORK_HEADERS.files = $$HEADERS FRAMEWORK_HEADERS.path = Headers/drumstick QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS #QMAKE_LFLAGS_SONAME = -Wl,-install_name,@executable_path/../Frameworks/ QMAKE_SONAME_PREFIX = @rpath QMAKE_TARGET_BUNDLE_PREFIX = net.sourceforge QMAKE_BUNDLE = drumstick-file QMAKE_INFO_PLIST = ../Info.plist.lib } drumstick-2.9.0/library/file/CMakeLists.txt0000644000175000017500000001054514541630232017663 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-file_QTOBJ_SRCS ../include/drumstick/qsmf.h ../include/drumstick/qwrk.h ../include/drumstick/rmid.h ) set(drumstick-file_HEADERS ../include/drumstick/macros.h ../include/drumstick/qsmf.h ../include/drumstick/qwrk.h ../include/drumstick/rmid.h ) if(BUILD_FRAMEWORKS) set_source_files_properties(${drumstick-file_HEADERS} PROPERTIES MACOSX_PACKAGE_LOCATION Headers/drumstick ) endif() set(drumstick-file_SRCS qsmf.cpp qwrk.cpp rmid.cpp ) if (WIN32) set(TARGET_DESCRIPTION ${Drumstick_DESCRIPTION}) set(TARGET_NAME drumstick-file) set(TARGET_ORIGINAL_FILENAME libdrumstick-file.dll) configure_file(${Drumstick_SOURCE_DIR}/versioninfo.rc.in versioninfo.rc @ONLY) list(APPEND drumstick-file_SRCS versioninfo.rc) endif() if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-file_MOC_SRCS ${drumstick-file_QTOBJ_SRCS}) else() qt_wrap_cpp(drumstick-file_MOC_SRCS ${drumstick-file_QTOBJ_SRCS}) endif() add_library(drumstick-file ${drumstick-file_MOC_SRCS} ${drumstick-file_SRCS} ${drumstick-file_HEADERS} ) add_library(Drumstick::File ALIAS drumstick-file) target_include_directories(drumstick-file PUBLIC $ $ ) target_link_libraries(drumstick-file PRIVATE Qt${QT_VERSION_MAJOR}::Core ) target_compile_definitions(drumstick-file PRIVATE QT_NO_SIGNALS_SLOTS_KEYWORDS ) if (QT_VERSION VERSION_GREATER_EQUAL 6.0) target_link_libraries(drumstick-file PRIVATE Qt6::Core5Compat ) endif() if(STATIC_DRUMSTICK) set_target_properties(drumstick-file PROPERTIES STATIC_LIB "libdrumstick-file" EXPORT_NAME File ) else() set_target_properties(drumstick-file PROPERTIES VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH} SOVERSION ${PROJECT_VERSION_MAJOR} MACOSX_RPATH TRUE EXPORT_NAME File ) if(BUILD_FRAMEWORKS) set_target_properties(drumstick-file PROPERTIES FRAMEWORK ${BUILD_FRAMEWORKS} FRAMEWORK_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH} MACOSX_FRAMEWORK_IDENTIFIER "net.sourceforge.drumstick-file" MACOSX_FRAMEWORK_INFO_PLIST "${CMAKE_SOURCE_DIR}/cmake_admin/CustomFrameworkInfo.plist.in" ) endif() endif() install(TARGETS drumstick-file EXPORT drumstick-file-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} ) install(FILES ${drumstick-file_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/drumstick) install(EXPORT drumstick-file-targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/drumstick NAMESPACE Drumstick:: ) export(EXPORT drumstick-file-targets NAMESPACE Drumstick:: FILE ${CMAKE_BINARY_DIR}/drumstick-file-targets.cmake ) include(CMakePackageConfigHelpers) write_basic_package_version_file(${CMAKE_BINARY_DIR}/drumstick-file-config-version.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY AnyNewerVersion ) configure_file( drumstick-file-config.cmake ${CMAKE_BINARY_DIR}/drumstick-file-config.cmake @ONLY ) install(FILES ${CMAKE_BINARY_DIR}/drumstick-file-config-version.cmake ${CMAKE_BINARY_DIR}/drumstick-file-config.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/drumstick ) drumstick-2.9.0/library/file/qsmf.cpp0000644000175000017500000007200314541630232016572 0ustar pedropedro/* Standard MIDI File component Copyright (C) 2006-2023, Pedro Lopez-Cabanillas Based on midifile.c by Tim Thompson, M.Czeiszperger and Greg Lee This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include DISABLE_WARNING_PUSH DISABLE_WARNING_DEPRECATED_DECLARATIONS /** * @file qsmf.cpp * Implementation of a class managing Standard MIDI Files input/output */ namespace drumstick { namespace File { /** * @addtogroup SMF * @{ * * QSmf provides a mechanism to parse and encode Standard MIDI Files, without * the burden of a policy forcing to use some internal sequence representation. * * This class is not related or based on the ALSA library. * * @} */ class QSmf::QSmfPrivate { public: QSmfPrivate(): m_Interactive(false), m_CurrTime(0), m_RealTime(0), m_DblRealTime(0), m_DblOldRealtime(0), m_Division(96), m_CurrTempo(500000), m_OldCurrTempo(500000), m_OldRealTime(0), m_OldCurrTime(0), m_RevisedTime(0), m_TempoChangeTime(0), m_ToBeRead(0), m_NumBytesWritten(0), m_Tracks(0), m_fileFormat(0), m_LastStatus(0), m_codec(nullptr), m_IOStream(nullptr) { } bool m_Interactive; /**< file and track headers are not required */ quint64 m_CurrTime; /**< current time in delta-time units */ quint64 m_RealTime; /**< current time in 1/16 centisecond-time units */ double m_DblRealTime; /**< as above, floating */ double m_DblOldRealtime; int m_Division; /**< ticks per beat. Default = 96 */ quint64 m_CurrTempo; /**< microseconds per quarter note */ quint64 m_OldCurrTempo; quint64 m_OldRealTime; quint64 m_OldCurrTime; quint64 m_RevisedTime; quint64 m_TempoChangeTime; quint64 m_ToBeRead; quint64 m_NumBytesWritten; int m_Tracks; int m_fileFormat; int m_LastStatus; QTextCodec *m_codec; QDataStream *m_IOStream; QByteArray m_MsgBuff; QList m_TempoList; }; /** * Constructor * @param parent Optional parent object */ QSmf::QSmf(QObject * parent) : QObject(parent), d(new QSmfPrivate) { } /** * Destructor */ QSmf::~QSmf() { d->m_TempoList.clear(); } /** * Check if the SMF stream is positioned at the end. * @return True if the SMF stream is at the end */ bool QSmf::endOfSmf() { return d->m_IOStream->atEnd(); } /** * Gets a single byte from the SMF stream * @return A Single byte */ quint8 QSmf::getByte() { quint8 b = 0; if (!endOfSmf()) { *d->m_IOStream >> b; d->m_ToBeRead--; } return b; } /** * Puts a single byte to the SMF stream * @param value A Single byte */ void QSmf::putByte(quint8 value) { *d->m_IOStream << value; d->m_NumBytesWritten++; } /** * Adds a tempo change to the internal tempo list * @param tempo Tempo in microseconds per quarter * @param time Location in ticks */ void QSmf::addTempo(quint64 tempo, quint64 time) { QSmfRecTempo tempoRec; tempoRec.tempo = tempo; tempoRec.time = time; d->m_TempoList.append(tempoRec); } /** * Reads a SMF header */ void QSmf::readHeader() { d->m_CurrTime = 0; d->m_RealTime = 0; d->m_Division = 96; d->m_CurrTempo = 500000; d->m_OldCurrTempo = 500000; addTempo(d->m_CurrTempo, 0); if (d->m_Interactive) { d->m_fileFormat= 0; d->m_Tracks = 1; d->m_Division = 96; } else { readExpected("MThd"); d->m_ToBeRead = read32bit(); d->m_fileFormat = read16bit(); d->m_Tracks = read16bit(); d->m_Division = read16bit(); } Q_EMIT signalSMFHeader(d->m_fileFormat, d->m_Tracks, d->m_Division); /* flush any extra stuff, in case the length of header is not */ while ((d->m_ToBeRead > 0) && !endOfSmf()) { getByte(); } if (d->m_ToBeRead > 0) { SMFError("Unexpected end of input"); } } /** * Reads a track chunk */ void QSmf::readTrack() { /* This array is indexed by the high half of a status byte. It's value is either the number of bytes needed (1 or 2) for a channel message, or 0 (meaning it's not a channel message). */ static const quint8 chantype[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0 }; quint64 lookfor; quint8 c, c1, type; bool sysexcontinue; // true if last message was an unfinished SysEx bool running; // true when running status is used quint8 status; // status value (e.g. 0x90==note-on) int needed; double delta_secs; quint64 delta_ticks, save_time, save_tempo; sysexcontinue = false; status = 0; if (d->m_Interactive) { d->m_ToBeRead = std::numeric_limits::max(); } else { readExpected("MTrk"); d->m_ToBeRead = read32bit(); } d->m_CurrTime = 0; d->m_RealTime = 0; d->m_DblRealTime = 0; d->m_DblOldRealtime = 0; d->m_OldCurrTime = 0; d->m_OldRealTime = 0; d->m_CurrTempo = findTempo(); Q_EMIT signalSMFTrackStart(); while (!endOfSmf() && (d->m_Interactive || d->m_ToBeRead > 0)) { lookfor = 0; if (d->m_Interactive) { d->m_CurrTime++; } else { delta_ticks = unsigned(readVarLen()); d->m_RevisedTime = d->m_CurrTime; d->m_CurrTime += delta_ticks; while (d->m_RevisedTime < d->m_CurrTime) { save_time = d->m_RevisedTime; save_tempo = d->m_CurrTempo; d->m_CurrTempo = findTempo(); if (d->m_CurrTempo != d->m_OldCurrTempo) { d->m_OldCurrTempo = d->m_CurrTempo; d->m_OldRealTime = d->m_RealTime; if (d->m_RevisedTime != d->m_TempoChangeTime) { d->m_DblOldRealtime = d->m_DblRealTime; d->m_OldCurrTime = save_time; } delta_secs = ticksToSecs(d->m_RevisedTime - d->m_OldCurrTime, quint16(d->m_Division), save_tempo); d->m_DblRealTime = d->m_DblOldRealtime + delta_secs * 1600.0; d->m_RealTime = llround(d->m_DblRealTime); if (d->m_RevisedTime == d->m_TempoChangeTime) { d->m_OldCurrTime = d->m_RevisedTime; d->m_DblOldRealtime = d->m_DblRealTime; } } else { delta_secs = ticksToSecs(d->m_RevisedTime - d->m_OldCurrTime, quint16(d->m_Division), d->m_CurrTempo); d->m_DblRealTime = d->m_DblOldRealtime + delta_secs * 1600.0; d->m_RealTime = llround(d->m_DblRealTime); } } } c = getByte(); if (sysexcontinue && (c != end_of_sysex)) { SMFError("didn't find expected continuation of a SysEx"); } if (c < 0xf8) { if ((c & 0x80) == 0) { if (status == 0) { SMFError("unexpected running status"); } running = true; } else { status = c; running = false; } needed = chantype[status >> 4 & 0x0f]; if (needed != 0) { if (running) { c1 = c; } else { c1 = getByte(); } if (needed > 1) { channelMessage(status, c1, getByte()); } else { channelMessage(status, c1, 0); } continue; } } switch (c) { case meta_event: type = getByte(); lookfor = quint64(readVarLen()); lookfor = d->m_ToBeRead - lookfor; msgInit(); while ((d->m_ToBeRead > lookfor) && !endOfSmf()) { msgAdd(getByte()); } metaEvent(type); break; case system_exclusive: lookfor = quint64(readVarLen()); lookfor = d->m_ToBeRead - lookfor; msgInit(); msgAdd(system_exclusive); while ((d->m_ToBeRead > lookfor) && !endOfSmf()) { c = getByte(); msgAdd(c); } if (c == end_of_sysex) { sysEx(); } else { sysexcontinue = true; } break; case end_of_sysex: lookfor = readVarLen(); lookfor = d->m_ToBeRead - lookfor; if (!sysexcontinue) { msgInit(); } while ((d->m_ToBeRead > lookfor) && !endOfSmf()) { c = getByte(); msgAdd(c); } if (sysexcontinue) { if (c == end_of_sysex) { sysEx(); sysexcontinue = false; } } break; default: badByte(c, d->m_IOStream->device()->pos() - 1); break; } if ((d->m_ToBeRead > lookfor) && endOfSmf()) { SMFError("Unexpected end of input"); } } if (d->m_ToBeRead > 0) { SMFError(QStringLiteral("Track ended before reading last %1 bytes").arg(d->m_ToBeRead)); } Q_EMIT signalSMFTrackEnd(); } /** * Reads a SMF stream. */ void QSmf::SMFRead() { int i; readHeader(); for ( i = d->m_Tracks; (i > 0) && !endOfSmf(); i--) { readTrack(); } if (i > 0) { SMFError( QStringLiteral("%1 tracks out of a total of %2 are missing").arg(i).arg(d->m_Tracks)); } } /** * Writes a SMF stream. * * Every MIDI file starts with a header. * In format 1 files, the first track is a tempo map. * The rest of the file is a series of tracks */ void QSmf::SMFWrite() { int i; d->m_LastStatus = 0; writeHeaderChunk(d->m_fileFormat, d->m_Tracks, d->m_Division); d->m_LastStatus = 0; if (d->m_fileFormat == 1) { Q_EMIT signalSMFWriteTempoTrack(); } for (i = 0; i < d->m_Tracks; ++i) { writeTrackChunk(i); } } /** * Reads a SMF stream. * @param stream Pointer to an existing and opened stream */ void QSmf::readFromStream(QDataStream *stream) { d->m_IOStream = stream; SMFRead(); } /** * Reads a SMF stream from a disk file. * @param fileName Name of an existing file. */ void QSmf::readFromFile(const QString& fileName) { QFile file(fileName); file.open(QIODevice::ReadOnly); QDataStream ds(&file); readFromStream(&ds); file.close(); } /** * Writes a SMF stream * @param stream Pointer to an existing and opened stream */ void QSmf::writeToStream(QDataStream *stream) { d->m_IOStream = stream; SMFWrite(); } /** * Writes a SMF stream to a disk file * @param fileName File name */ void QSmf::writeToFile(const QString& fileName) { QFile file(fileName); file.open(QIODevice::WriteOnly); QDataStream ds(&file); writeToStream(&ds); file.close(); } /** * Writes a SMF header chuck * @param format SMF Format (0/1/2) * @param ntracks Number of tracks * @param division Resolution in ticks per quarter note */ void QSmf::writeHeaderChunk(int format, int ntracks, int division) { write32bit(MThd); write32bit(6); write16bit(quint16(format)); write16bit(quint16(ntracks)); write16bit(quint16(division)); } /** * Writes a track chuck * @param track Number of the track */ void QSmf::writeTrackChunk(int track) { quint32 trkhdr; quint32 trklength; qint64 offset; qint64 place_marker; d->m_LastStatus = 0; trkhdr = MTrk; trklength = 0; offset = d->m_IOStream->device()->pos(); write32bit(trkhdr); write32bit(trklength); d->m_NumBytesWritten = 0; Q_EMIT signalSMFWriteTrack(track); place_marker = d->m_IOStream->device()->pos(); d->m_IOStream->device()->seek(offset); trklength = d->m_NumBytesWritten; write32bit(trkhdr); write32bit(trklength); d->m_IOStream->device()->seek(place_marker); } /** * Writes a variable length Meta Event * @param deltaTime Time offset in ticks * @param type Meta event type * @param data Message data */ void QSmf::writeMetaEvent(long deltaTime, int type, const QByteArray& data) { writeVarLen(deltaTime); d->m_LastStatus = meta_event; putByte(d->m_LastStatus); putByte(type); writeVarLen(data.size()); foreach(char byte, data) putByte(byte); } /** * Writes a Text Meta Event * @param deltaTime Time offset in ticks * @param type Meta event type * @param data Message text */ void QSmf::writeMetaEvent(long deltaTime, int type, const QString& data) { writeVarLen(deltaTime); putByte(d->m_LastStatus = meta_event); putByte(type); QByteArray lcldata; if (d->m_codec == nullptr) lcldata = data.toLatin1(); else lcldata = d->m_codec->fromUnicode(data); writeVarLen(lcldata.length()); foreach(char byte, lcldata) putByte(byte); } /** * Writes a simple Meta event * @param deltaTime Time offset in ticks * @param type Meta event type * @param data Meta event data * @since 0.2.0 */ void QSmf::writeMetaEvent(long deltaTime, int type, int data) { writeVarLen(deltaTime); putByte(d->m_LastStatus = meta_event); putByte(type); putByte(1); putByte(data); } /** * Writes a simple Meta event * @param deltaTime Time offset in ticks * @param type Meta event type */ void QSmf::writeMetaEvent(long deltaTime, int type) { writeVarLen(deltaTime); putByte(d->m_LastStatus = meta_event); putByte(type); putByte(0); } /** * Writes a variable length MIDI message * @param deltaTime Time offset in ticks * @param type MIDI event type * @param chan MIDI Channel * @param data Message data */ void QSmf::writeMidiEvent(long deltaTime, int type, int chan, const QByteArray& data) { unsigned int i, j, size; quint8 c; writeVarLen(quint64(deltaTime)); if ((type == system_exclusive) || (type == end_of_sysex)) { c = type; d->m_LastStatus = 0; } else { if (chan > 15) { SMFError("error: MIDI channel greater than 16"); } c = type | chan; } if (d->m_LastStatus != c) { d->m_LastStatus = c; putByte(c); } c = quint8(data[0]); if (type == system_exclusive || type == end_of_sysex) { size = data.size(); if (type == c) --size; writeVarLen(size); } j = (c == type ? 1 : 0); for (i = j; i < unsigned(data.size()); ++i) { putByte(quint8(data[i])); } } /** * Writes a MIDI message with a single parameter * @param deltaTime Time offset in ticks * @param type MIDI event type * @param chan MIDI Channel * @param b1 Message parameter */ void QSmf::writeMidiEvent(long deltaTime, int type, int chan, int b1) { quint8 c; writeVarLen(deltaTime); if ((type == system_exclusive) || (type == end_of_sysex)) { SMFError("error: Wrong method for a system exclusive event"); } if (chan > 15) { SMFError("error: MIDI channel greater than 16"); } c = type | chan; if (d->m_LastStatus != c) { d->m_LastStatus = c; putByte(c); } putByte(b1); } /** * Writes a MIDI message with two parameters * @param deltaTime Time offset in ticks * @param type MIDI event type * @param chan MIDI Channel * @param b1 Message parameter 1 * @param b2 Message parameter 2 */ void QSmf::writeMidiEvent(long deltaTime, int type, int chan, int b1, int b2) { quint8 c; writeVarLen(deltaTime); if ((type == system_exclusive) || (type == end_of_sysex)) { SMFError("error: Wrong method for a system exclusive event"); } if (chan > 15) { SMFError("error: MIDI channel greater than 16"); } c = type | chan; if (d->m_LastStatus != c) { d->m_LastStatus = c; putByte(c); } putByte(b1); putByte(b2); } /** * Writes a variable length MIDI message * @param deltaTime Time offset in ticks * @param type MIDI event type * @param len Message length * @param data Message data */ void QSmf::writeMidiEvent(long deltaTime, int type, long len, char* data) { unsigned int i, j, size; quint8 c; writeVarLen(quint64(deltaTime)); if ((type != system_exclusive) && (type != end_of_sysex)) { SMFError("error: type should be system exclusive"); } d->m_LastStatus = 0; c = quint8(type); putByte(c); size = unsigned(len); c = quint8(data[0]); if (c == type) --size; writeVarLen(size); j = (c == type ? 1 : 0); for (i = j; i < unsigned(len); ++i) { putByte(quint8(data[i])); } } /** * Writes a MIDI Sequence number * @param deltaTime Time offset in ticks * @param seqnum Sequence number */ void QSmf::writeSequenceNumber(long deltaTime, int seqnum) { writeVarLen(deltaTime); d->m_LastStatus = meta_event; putByte(d->m_LastStatus); putByte(sequence_number); putByte(2); putByte((seqnum >> 8) & 0xff); putByte(seqnum & 0xff); } /** * Writes a Tempo change message * @param deltaTime Time offset in ticks * @param tempo Tempo in microseconds per quarter note */ void QSmf::writeTempo(long deltaTime, long tempo) { writeVarLen(deltaTime); putByte(d->m_LastStatus = meta_event); putByte(set_tempo); putByte(3); putByte((tempo >> 16) & 0xff); putByte((tempo >> 8) & 0xff); putByte(tempo & 0xff); } /** * Writes a Tempo change message * @param deltaTime Time offset in ticks * @param tempo Tempo expressed in quarter notes per minute */ void QSmf::writeBpmTempo(long deltaTime, int tempo) { long us_tempo = 60000000l / tempo; writeTempo(deltaTime, us_tempo); } /** * Writes a Time Signature message * @param deltaTime Time offset in ticks * @param num Numerator * @param den Denominator (exponent for a power of two) * @param cc Number of MIDI clocks in a metronome click * @param bb Number of notated 32nd notes in 24 MIDI clocks */ void QSmf::writeTimeSignature(long deltaTime, int num, int den, int cc, int bb) { writeVarLen(deltaTime); putByte(d->m_LastStatus = meta_event); putByte(time_signature); putByte(4); putByte(num & 0xff); putByte(den & 0xff); putByte(cc & 0xff); putByte(bb & 0xff); } /** * Writes a key Signature message * @param deltaTime Time offset in ticks * @param tone Number of alterations (positive=sharps, negative=flats) * @param mode Scale mode (0=major, 1=minor) */ void QSmf::writeKeySignature(long deltaTime, int tone, int mode) { writeVarLen(quint64(deltaTime)); putByte(d->m_LastStatus = meta_event); putByte(key_signature); putByte(2); putByte(quint8(tone)); putByte(mode & 0x01); } /** * Writes multi-length bytes * @param value Integer value */ void QSmf::writeVarLen(quint64 value) { quint64 buffer; buffer = value & 0x7f; while ((value >>= 7) > 0) { buffer <<= 8; buffer |= 0x80; buffer += (value & 0x7f); } while (true) { putByte(buffer & 0xff); if (buffer & 0x80) buffer >>= 8; else break; } } /* These routines are used to make sure that the byte order of the various data types remains constant between machines. */ void QSmf::write32bit(quint32 data) { putByte((data >> 24) & 0xff); putByte((data >> 16) & 0xff); putByte((data >> 8) & 0xff); putByte(data & 0xff); } void QSmf::write16bit(quint16 data) { putByte((data >> 8) & 0xff); putByte(data & 0xff); } quint16 QSmf::to16bit(quint8 c1, quint8 c2) { quint16 value; value = quint16(c1 << 8); value += c2; return value; } quint32 QSmf::to32bit(quint8 c1, quint8 c2, quint8 c3, quint8 c4) { quint32 value; value = unsigned(c1 << 24); value += unsigned(c2 << 16); value += unsigned(c3 << 8); value += c4; return value; } quint16 QSmf::read16bit() { quint8 c1, c2; c1 = getByte(); c2 = getByte(); return to16bit(c1, c2); } quint32 QSmf::read32bit() { quint8 c1, c2, c3, c4; c1 = getByte(); c2 = getByte(); c3 = getByte(); c4 = getByte(); return to32bit(c1, c2, c3, c4); } long QSmf::readVarLen() { quint64 value; quint8 c; c = getByte(); value = c; if ((c & 0x80) != 0) { value &= 0x7f; do { c = getByte(); value = (value << 7) + (c & 0x7f); } while ((c & 0x80) != 0); } return long(value); } void QSmf::readExpected(const QString& s) { int j; quint8 b; for (j = 0; j < s.length(); ++j) { b = getByte(); if (QChar(b) != s[j]) { SMFError(QString("Invalid (%1) SMF format at %2").arg(b, 0, 16).arg(d->m_IOStream->device()->pos())); break; } } } quint64 QSmf::findTempo() { quint64 result, old_tempo, new_tempo; QSmfRecTempo rec = d->m_TempoList.last(); old_tempo = d->m_CurrTempo; new_tempo = d->m_CurrTempo; QList::Iterator it; for( it = d->m_TempoList.begin(); it != d->m_TempoList.end(); ++it ) { rec = (*it); if (rec.time <= d->m_CurrTime) { old_tempo = rec.tempo; } new_tempo = rec.tempo; if (rec.time > d->m_RevisedTime) { break; } } if ((rec.time <= d->m_RevisedTime) || (rec.time > d->m_CurrTime)) { d->m_RevisedTime = d->m_CurrTime; result = old_tempo; } else { d->m_RevisedTime = rec.time; d->m_TempoChangeTime = d->m_RevisedTime; result = new_tempo; } return result; } /* This routine converts delta times in ticks into seconds. The else statement is needed because the formula is different for tracks based on notes and tracks based on SMPTE times. */ double QSmf::ticksToSecs(quint64 ticks, quint16 division, quint64 tempo) { double result; double smpte_format; double smpte_resolution; if (division > 0) { result = double(ticks * tempo)/(division * 1000000.0); } else { smpte_format = upperByte(division); smpte_resolution = lowerByte(division); result = double(ticks)/(smpte_format * smpte_resolution * 1000000.0); } return result; } void QSmf::SMFError(const QString& s) { Q_EMIT signalSMFError(s); } void QSmf::channelMessage(quint8 status, quint8 c1, quint8 c2) { quint8 chan; int k; chan = status & midi_channel_mask; if (c1 > 127) { SMFError(QString("ChannelMessage with bad c1 = %1").arg(c1)); //c1 &= 127; } if (c2 > 127) { SMFError(QString("ChannelMessage with bad c2 = %1").arg(c2)); //c2 &= 127; } switch (status & midi_command_mask) { case note_off: Q_EMIT signalSMFNoteOff(chan, c1, c2); break; case note_on: Q_EMIT signalSMFNoteOn(chan, c1, c2); break; case poly_aftertouch: Q_EMIT signalSMFKeyPress(chan, c1, c2); break; case control_change: Q_EMIT signalSMFCtlChange(chan, c1, c2); break; case program_chng: Q_EMIT signalSMFProgram(chan, c1); break; case channel_aftertouch: Q_EMIT signalSMFChanPress(chan, c1); break; case pitch_wheel: k = c1 + (c2 << 7) - 8192; Q_EMIT signalSMFPitchBend(chan, k); break; default: SMFError(QString("Invalid MIDI status %1. Unhandled event").arg(status)); break; } } void QSmf::metaEvent(quint8 b) { QSmfRecTempo rec; QByteArray m(d->m_MsgBuff); switch (b) { case sequence_number: Q_EMIT signalSMFSequenceNum(to16bit(m[0], m[1])); break; case text_event: case copyright_notice: case sequence_name: case instrument_name: case lyric: case marker: case cue_point: { QString s; if (d->m_codec == nullptr) { Q_EMIT signalSMFText2(b, m); } else { s = d->m_codec->toUnicode(m); Q_EMIT signalSMFText(b, s); } } break; case forced_channel: Q_EMIT signalSMFforcedChannel(m[0]); break; case forced_port: Q_EMIT signalSMFforcedPort(m[0]); break; case end_of_track: Q_EMIT signalSMFendOfTrack(); break; case set_tempo: d->m_CurrTempo = to32bit(0, m[0], m[1], m[2]); Q_EMIT signalSMFTempo(d->m_CurrTempo); rec = d->m_TempoList.last(); if (rec.tempo == d->m_CurrTempo) { return; } if (rec.time > d->m_CurrTime) { return; } addTempo(d->m_CurrTempo, d->m_CurrTime); break; case smpte_offset: Q_EMIT signalSMFSmpte(m[0], m[1], m[2], m[3], m[4]); break; case time_signature: Q_EMIT signalSMFTimeSig(m[0], m[1], m[2], m[3]); break; case key_signature: Q_EMIT signalSMFKeySig(m[0], m[1]); break; case sequencer_specific: Q_EMIT signalSMFSeqSpecific(m); break; default: Q_EMIT signalSMFMetaUnregistered(b, m); break; } Q_EMIT signalSMFMetaMisc(b, m); } void QSmf::sysEx() { QByteArray varr(d->m_MsgBuff); Q_EMIT signalSMFSysex(varr); } void QSmf::badByte(quint8 b, int p) { SMFError(QString("Unexpected byte (%1) at %2").arg(b, 2, 16).arg(p)); } quint8 QSmf::lowerByte(quint16 x) { return (x & 0xff); } quint8 QSmf::upperByte(quint16 x) { return ((x >> 8) & 0xff); } void QSmf::msgInit() { d->m_MsgBuff.truncate(0); } void QSmf::msgAdd(quint8 b) { int s = d->m_MsgBuff.size(); d->m_MsgBuff.resize(s + 1); d->m_MsgBuff[s] = b; } /* public properties (accessors) */ /** * Gets the current time in ticks * @return Time in ticks */ long QSmf::getCurrentTime() { return d->m_CurrTime; } /** * Gets the current tempo * @return Tempo in us per quarter */ long QSmf::getCurrentTempo() { return d->m_CurrTempo; } /** * Gets the real time in seconds * @return Time in seconds */ long QSmf::getRealTime() { return d->m_RealTime; } /** * Gets the resolution * @return Resolution in ticks per quarter note */ int QSmf::getDivision() { return d->m_Division; } /** * Sets the resolution * @param division Resolution in ticks per quarter note */ void QSmf::setDivision(int division) { d->m_Division = division; } /** * Gets the number of tracks * @return Number of tracks */ int QSmf::getTracks() { return d->m_Tracks; } /** * Sets the number of tracks * @param tracks Number of tracks */ void QSmf::setTracks(int tracks) { d->m_Tracks = tracks; } /** * Gets the SMF file format * @return File format (0, 1, or 2) */ int QSmf::getFileFormat() { return d->m_fileFormat; } /** * Sets the SMF file format * @param fileFormat File format (0, 1, or 2) */ void QSmf::setFileFormat(int fileFormat) { d->m_fileFormat = fileFormat; } /** * Gets the position in the SMF stream * @return Position offset in the stream */ long QSmf::getFilePos() { return long(d->m_IOStream->device()->pos()); } /** * Gets the text codec used for text meta-events I/O * @return QTextCodec pointer * @since 0.2.0 * @deprecated because the class QTextCodec was removed from QtCore since Qt6. */ QTextCodec* QSmf::getTextCodec() { return d->m_codec; } /** * Sets the text codec for text meta-events. * The engine doesn't take ownership of the codec instance. * * @param codec QTextCodec pointer * @since 0.2.0 * @deprecated because the class QTextCodec was removed from QtCore since Qt6. */ void QSmf::setTextCodec(QTextCodec *codec) { d->m_codec = codec; } /** * @brief drumstickLibraryVersion provides the Drumstick version as an edited QString * @return Drumstick library version */ QString drumstickLibraryVersion() { return QStringLiteral(QT_STRINGIFY(VERSION)); } } // namespace File } // namespace drumstick DISABLE_WARNING_POP drumstick-2.9.0/library/file/qwrk.cpp0000644000175000017500000010442314541630232016612 0ustar pedropedro/* WRK File component Copyright (C) 2010-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include DISABLE_WARNING_PUSH DISABLE_WARNING_DEPRECATED_DECLARATIONS /** * @file qwrk.cpp * Implementation of a class managing Cakewalk WRK Files input */ namespace drumstick { namespace File { /** * @addtogroup WRK * @{ * * QWrk provides a mechanism to parse Cakewalk WRK Files, without * the burden of a policy forcing to use some internal sequence representation. * * This class is not related or based on the ALSA library. * * @} */ class QWrk::QWrkPrivate { public: QWrkPrivate(): m_Now(0), m_From(0), m_Thru(11930), m_KeySig(0), m_Clock(0), m_AutoSave(0), m_PlayDelay(0), m_ZeroCtrls(false), m_SendSPP(true), m_SendCont(true), m_PatchSearch(false), m_AutoStop(false), m_StopTime(4294967295U), m_AutoRewind(false), m_RewindTime(0), m_MetroPlay(false), m_MetroRecord(true), m_MetroAccent(false), m_CountIn(1), m_ThruOn(true), m_AutoRestart(false), m_CurTempoOfs(1), m_TempoOfs1(32), m_TempoOfs2(64), m_TempoOfs3(128), m_PunchEnabled(false), m_PunchInTime(0), m_PunchOutTime(0), m_EndAllTime(0), m_division(120), m_codec(nullptr), m_IOStream(nullptr) { } quint32 m_Now; ///< Now marker time quint32 m_From; ///< From marker time quint32 m_Thru; ///< Thru marker time quint8 m_KeySig; ///< Key signature (0=C, 1=C#, ... 11=B) quint8 m_Clock; ///< Clock Source (0=Int, 1=MIDI, 2=FSK, 3=SMPTE) quint8 m_AutoSave; ///< Auto save (0=disabled, 1..256=minutes) quint8 m_PlayDelay; ///< Play Delay bool m_ZeroCtrls; ///< Zero continuous controllers? bool m_SendSPP; ///< Send Song Position Pointer? bool m_SendCont; ///< Send MIDI Continue? bool m_PatchSearch; ///< Patch/controller search-back? bool m_AutoStop; ///< Auto-stop? quint32 m_StopTime; ///< Auto-stop time bool m_AutoRewind; ///< Auto-rewind? quint32 m_RewindTime; ///< Auto-rewind time bool m_MetroPlay; ///< Metronome on during playback? bool m_MetroRecord; ///< Metronome on during recording? bool m_MetroAccent; ///< Metronome accents primary beats? quint8 m_CountIn; ///< Measures of count-in (0=no count-in) bool m_ThruOn; ///< MIDI Thru enabled? (only used if no THRU rec) bool m_AutoRestart; ///< Auto-restart? quint8 m_CurTempoOfs; ///< Which of the 3 tempo offsets is used: 0..2 quint8 m_TempoOfs1; ///< Fixed-point ratio value of offset 1 quint8 m_TempoOfs2; ///< Fixed-point ratio value of offset 2 quint8 m_TempoOfs3; ///< Fixed-point ratio value of offset 3 bool m_PunchEnabled; ///< Auto-Punch enabled? quint32 m_PunchInTime; ///< Punch-in time quint32 m_PunchOutTime; ///< Punch-out time quint32 m_EndAllTime; ///< Time of latest event (incl. all tracks) int m_division; QTextCodec *m_codec; QDataStream *m_IOStream; QByteArray m_lastChunkData; QList m_tempos; qint64 m_lastChunkPos; qint64 internalFilePos(); }; /** * Constructor * @param parent Object owner */ QWrk::QWrk(QObject * parent) : QObject(parent), d(new QWrkPrivate) { } /** * Destructor */ QWrk::~QWrk() { } /** * Gets the text codec used for text meta-events I/O. * * @return QTextCodec pointer * @deprecated because the class QTextCodec was removed from QtCore since Qt6. */ QTextCodec* QWrk::getTextCodec() { return d->m_codec; } /** * Sets the text codec for text meta-events. * The engine doesn't take ownership of the codec instance. * * @param codec QTextCodec pointer * @deprecated because the class QTextCodec was removed from QtCore since Qt6. */ void QWrk::setTextCodec(QTextCodec *codec) { d->m_codec = codec; } /** * Gets the last chunk raw data (undecoded) * * @return last chunk raw data */ QByteArray QWrk::getLastChunkRawData() const { return d->m_lastChunkData; } /** * Read the chunk raw data (undecoded) */ void QWrk::readRawData(int size) { if (size > 0) { d->m_lastChunkData = d->m_IOStream->device()->read(size); } else { d->m_lastChunkData.clear(); //qDebug() << Q_FUNC_INFO << "Size error:" << size; } } /** * Now marker time * @return Now marker time */ int QWrk::getNow() const { return d->m_Now; } /** * From marker time * @return From marker time */ int QWrk::getFrom() const { return d->m_From; } /** * Thru marker time * @return Thru marker time */ int QWrk::getThru() const { return d->m_Thru; } /** * Key signature (0=C, 1=C#, ... 11=B) * @return Key signature */ int QWrk::getKeySig() const { return d->m_KeySig; } /** * Clock Source (0=Int, 1=MIDI, 2=FSK, 3=SMPTE) * @return Clock Source */ int QWrk::getClock() const { return d->m_Clock; } /** * Auto save (0=disabled, 1..256=minutes) * @return Auto save */ int QWrk::getAutoSave() const { return d->m_AutoSave; } /** * Play Delay * @return Play Delay */ int QWrk::getPlayDelay() const { return d->m_PlayDelay; } /** * Zero continuous controllers? * @return Zero continuous controllers */ bool QWrk::getZeroCtrls() const { return d->m_ZeroCtrls; } /** * Send Song Position Pointer? * @return Send Song Position Pointer */ bool QWrk::getSendSPP() const { return d->m_SendSPP; } /** * Send MIDI Continue? * @return Send MIDI Continue */ bool QWrk::getSendCont() const { return d->m_SendCont; } /** * Patch/controller search-back? * @return Patch/controller search-back */ bool QWrk::getPatchSearch() const { return d->m_PatchSearch; } /** * Auto-stop? * @return Auto-stop */ bool QWrk::getAutoStop() const { return d->m_AutoStop; } /** * Auto-stop time * @return Auto-stop time */ unsigned int QWrk::getStopTime() const { return d->m_StopTime; } /** * Auto-rewind? * @return Auto-rewind */ bool QWrk::getAutoRewind() const { return d->m_AutoRewind; } /** * Auto-rewind time * @return Auto-rewind time */ int QWrk::getRewindTime() const { return d->m_RewindTime; } /** * Metronome on during playback? * @return Metronome on during playback */ bool QWrk::getMetroPlay() const { return d->m_MetroPlay; } /** * Metronome on during recording? * @return Metronome on during recording */ bool QWrk::getMetroRecord() const { return d->m_MetroRecord; } /** * Metronome accents primary beats? * @return Metronome accents primary beats */ bool QWrk::getMetroAccent() const { return d->m_MetroAccent; } /** * Measures of count-in (0=no count-in) * @return Measures of count-in */ int QWrk::getCountIn() const { return d->m_CountIn; } /** * MIDI Thru enabled? (only used if no THRU rec) * @return MIDI Thru enabled */ bool QWrk::getThruOn() const { return d->m_ThruOn; } /** * Auto-restart? * @return Auto-restart */ bool QWrk::getAutoRestart() const { return d->m_AutoRestart; } /** * Which of the 3 tempo offsets is used: 0..2 * @return tempo offset index */ int QWrk::getCurTempoOfs() const { return d->m_CurTempoOfs; } /** * Fixed-point ratio value of tempo offset 1 * * NOTE: The offset ratios are expressed as a numerator in the expression * n/64. To get a ratio from this number, divide the number by 64. To get * this number from a ratio, multiply the ratio by 64. * Examples: * 32 ==> 32/64 = 0.5 * 63 ==> 63/64 = 0.9 * 64 ==> 64/64 = 1.0 * 128 ==> 128/64 = 2.0 * * @return tempo offset 1 */ int QWrk::getTempoOfs1() const { return d->m_TempoOfs1; } /** * Fixed-point ratio value of tempo offset 2 * * NOTE: The offset ratios are expressed as a numerator in the expression * n/64. To get a ratio from this number, divide the number by 64. To get * this number from a ratio, multiply the ratio by 64. * Examples: * 32 ==> 32/64 = 0.5 * 63 ==> 63/64 = 0.9 * 64 ==> 64/64 = 1.0 * 128 ==> 128/64 = 2.0 * * @return tempo offset 2 */ int QWrk::getTempoOfs2() const { return d->m_TempoOfs2; } /** * Fixed-point ratio value of tempo offset 3 * * NOTE: The offset ratios are expressed as a numerator in the expression * n/64. To get a ratio from this number, divide the number by 64. To get * this number from a ratio, multiply the ratio by 64. * Examples: * 32 ==> 32/64 = 0.5 * 63 ==> 63/64 = 0.9 * 64 ==> 64/64 = 1.0 * 128 ==> 128/64 = 2.0 * * @return tempo offset 3 */ int QWrk::getTempoOfs3() const { return d->m_TempoOfs3; } /** * Auto-Punch enabled? * @return Auto-Punch enabled */ bool QWrk::getPunchEnabled() const { return d->m_PunchEnabled; } /** * Punch-in time * @return punch-in time */ int QWrk::getPunchInTime() const { return d->m_PunchInTime; } /** * Punch-out time * @return Punch-out time */ int QWrk::getPunchOutTime() const { return d->m_PunchOutTime; } /** * Time of latest event (incl. all tracks) * @return Time of latest event */ int QWrk::getEndAllTime() const { return d->m_EndAllTime; } /** * Gets a single byte from the stream * @return A Single byte */ quint8 QWrk::readByte() { quint8 b = 0xff; if (!d->m_IOStream->atEnd()) *d->m_IOStream >> b; return b; } /** * Converts two bytes into a single 16-bit value * @param c1 first byte * @param c2 second byte * @return 16-bit value */ quint16 QWrk::to16bit(quint8 c1, quint8 c2) { quint16 value = (c1 << 8); value += c2; return value; } /** * Converts four bytes into a single 32-bit value * @param c1 1st byte * @param c2 2nd byte * @param c3 3rd byte * @param c4 4th byte * @return 32-bit value */ quint32 QWrk::to32bit(quint8 c1, quint8 c2, quint8 c3, quint8 c4) { quint32 value = (c1 << 24); value += (c2 << 16); value += (c3 << 8); value += c4; return value; } /** * Reads a 16-bit value * @return 16-bit value */ quint16 QWrk::read16bit() { quint8 c1, c2; c1 = readByte(); c2 = readByte(); return to16bit(c2, c1); } /** * Reads a 24-bit value * @return 32-bit value */ quint32 QWrk::read24bit() { quint8 c1, c2, c3; c1 = readByte(); c2 = readByte(); c3 = readByte(); return to32bit(0, c3, c2, c1); } /** * Reads a 32-bit value * @return 32-bit value */ quint32 QWrk::read32bit() { quint8 c1, c2, c3, c4; c1 = readByte(); c2 = readByte(); c3 = readByte(); c4 = readByte(); return to32bit(c4, c3, c2, c1); } /** * Reads a string assuming local encoding if getTextCodec() is null * @return a string */ QString QWrk::readString(int len) { QString s; if ( len > 0 ) { QByteArray data = readByteArray(len); if (d->m_codec == nullptr) { s = QString::fromLatin1(data); } else { s = d->m_codec->toUnicode(data); } } return s; } /** * Reads a string as a QByteArray (without decoding) * @return a string */ QByteArray QWrk::readByteArray(int len) { QByteArray data; if ( len > 0 ) { quint8 c = 0xff; for ( int i = 0; i < len && c != 0 && !atEnd(); ++i ) { c = readByte(); if ( c != 0) data += c; } } return data; } /** * Reads a variable length string (C-style) * (assuming local encoding if getTextCodec() is null) * @return a string */ QString QWrk::readVarString() { QString s; QByteArray data = readVarByteArray(); if (d->m_codec == nullptr) { s = QString::fromLatin1(data); } else { s = d->m_codec->toUnicode(data); } return s; } /** * Reads a variable length string (C-style) as a QByteArray (without decoding) * @return a string */ QByteArray QWrk::readVarByteArray() { QByteArray data; quint8 b; do { b = readByte(); if (b != 0) data += b; } while (b != 0 && !atEnd()); return data; } void QWrk::processMarkers() { int num = read32bit(); for (int i = 0; (i < num) && (d->internalFilePos() < d->m_lastChunkPos) && !atEnd(); ++i) { int smpte = readByte(); readGap(1); long time = read24bit(); readGap(5); int len = readByte(); if (d->m_codec == nullptr) { QByteArray data = readByteArray(len); Q_EMIT signalWRKMarker2(time, smpte, data); } else { QString name = readString(len); Q_EMIT signalWRKMarker(time, smpte, name); } } } /** * Current position in the data stream * @return current position */ long QWrk::getFilePos() { return d->internalFilePos(); } /** * Seeks to a new position in the data stream * @param pos new position */ void QWrk::seek(qint64 pos) { if (!d->m_IOStream->device()->seek(pos)) { //qDebug() << Q_FUNC_INFO << "Error, pos:" << pos; } } /** * Checks if the data stream pointer has reached the end position * @return true if the read pointer is at end */ bool QWrk::atEnd() { return d->m_IOStream->atEnd(); } /** * Jumps the given size in the data stream * @param size the gap size */ void QWrk::readGap(int size) { if ( size > 0) seek( d->internalFilePos() + size ); } /** * Reads a stream. * @param stream Pointer to an existing and opened stream */ void QWrk::readFromStream(QDataStream *stream) { d->m_IOStream = stream; wrkRead(); } /** * Reads a stream from a disk file. * @param fileName Name of an existing file. */ void QWrk::readFromFile(const QString& fileName) { QFile file(fileName); file.open(QIODevice::ReadOnly); QDataStream ds(&file); readFromStream(&ds); file.close(); } void QWrk::processTrackChunk() { int namelen; QString name[2]; QByteArray data[2]; int trackno; int channel; int pitch; int velocity; int port; bool selected; bool muted; bool loop; trackno = read16bit(); for(int i=0; i<2; ++i) { namelen = readByte(); if (d->m_codec == nullptr) { data[i] = readByteArray(namelen); } else { name[i] = readString(namelen); } } channel = readByte() & 0x0f; pitch = readByte(); velocity = readByte(); port = readByte(); quint8 flags = readByte(); selected = ((flags & 1) != 0); muted = ((flags & 2) != 0); loop = ((flags & 4) != 0); if (d->m_codec == nullptr) { Q_EMIT signalWRKTrack2( data[0], data[1], trackno, channel, pitch, velocity, port, selected, muted, loop ); } else { Q_EMIT signalWRKTrack( name[0], name[1], trackno, channel, pitch, velocity, port, selected, muted, loop ); } } void QWrk::processVarsChunk() { d->m_Now = read32bit(); d->m_From = read32bit(); d->m_Thru = read32bit(); d->m_KeySig = readByte(); d->m_Clock = readByte(); d->m_AutoSave = readByte(); d->m_PlayDelay = readByte(); readGap(1); d->m_ZeroCtrls = (readByte() != 0); d->m_SendSPP = (readByte() != 0); d->m_SendCont = (readByte() != 0); d->m_PatchSearch = (readByte() != 0); d->m_AutoStop = (readByte() != 0); d->m_StopTime = read32bit(); d->m_AutoRewind = (readByte() != 0); d->m_RewindTime = read32bit(); d->m_MetroPlay = (readByte() != 0); d->m_MetroRecord = (readByte() != 0); d->m_MetroAccent = (readByte() != 0); d->m_CountIn = readByte(); readGap(2); d->m_ThruOn = (readByte() != 0); readGap(19); d->m_AutoRestart = (readByte() != 0); d->m_CurTempoOfs = readByte(); d->m_TempoOfs1 = readByte(); d->m_TempoOfs2 = readByte(); d->m_TempoOfs3 = readByte(); readGap(2); d->m_PunchEnabled = (readByte() != 0); d->m_PunchInTime = read32bit(); d->m_PunchOutTime = read32bit(); d->m_EndAllTime = read32bit(); Q_EMIT signalWRKGlobalVars(); } void QWrk::processTimebaseChunk() { quint16 timebase = read16bit(); d->m_division = timebase; Q_EMIT signalWRKTimeBase(timebase); } void QWrk::processNoteArray(int track, int events) { quint32 time = 0; quint8 status = 0, data1 = 0, data2 = 0, i = 0; quint16 dur = 0; int value = 0, type = 0, channel = 0, len = 0; QString text; QByteArray data; for ( i = 0; (i < events) && (d->internalFilePos() < d->m_lastChunkPos) && !atEnd(); ++i ) { time = read24bit(); status = readByte(); dur = 0; if (status >= 0x90) { type = status & 0xf0; channel = status & 0x0f; data1 = readByte(); if (type == 0x90 || type == 0xA0 || type == 0xB0 || type == 0xE0) data2 = readByte(); if (type == 0x90) dur = read16bit(); switch (type) { case 0x90: Q_EMIT signalWRKNote(track, time, channel, data1, data2, dur); break; case 0xA0: Q_EMIT signalWRKKeyPress(track, time, channel, data1, data2); break; case 0xB0: Q_EMIT signalWRKCtlChange(track, time, channel, data1, data2); break; case 0xC0: Q_EMIT signalWRKProgram(track, time, channel, data1); break; case 0xD0: Q_EMIT signalWRKChanPress(track, time, channel, data1); break; case 0xE0: value = (data2 << 7) + data1 - 8192; Q_EMIT signalWRKPitchBend(track, time, channel, value); break; case 0xF0: Q_EMIT signalWRKSysexEvent(track, time, data1); break; } } else if (status == 5) { int code = read16bit(); len = read32bit(); if (d->m_codec == nullptr) { data = readByteArray(len); Q_EMIT signalWRKExpression2(track, time, code, data); } else { text = readString(len); Q_EMIT signalWRKExpression(track, time, code, text); } } else if (status == 6) { int code = read16bit(); dur = read16bit(); readGap(4); Q_EMIT signalWRKHairpin(track, time, code, dur); } else if (status == 7) { len = read32bit(); text = readString(len); data.clear(); for(int j=0; j<13; ++j) { int byte = readByte(); data += byte; } Q_EMIT signalWRKChord(track, time, text, data); } else if (status == 8) { len = read16bit(); data.clear(); for(int j=0; jm_codec == nullptr) { data = readByteArray(len); Q_EMIT signalWRKText2(track, time, status, data); } else { text = readString(len); Q_EMIT signalWRKText(track, time, status, text); } } } if ((i < events) && atEnd()) { Q_EMIT signalWRKError("Corrupted file"); } Q_EMIT signalWRKStreamEnd(time + dur); } void QWrk::processStreamChunk() { long time = 0; int dur = 0, value = 0, type = 0, channel = 0, i = 0; quint8 status = 0, data1 = 0, data2 = 0; quint16 track = read16bit(); int events = read16bit(); for ( i = 0; (i < events) && (d->internalFilePos() < d->m_lastChunkPos) && !atEnd(); ++i ) { time = read24bit(); status = readByte(); data1 = readByte(); data2 = readByte(); dur = read16bit(); type = status & 0xf0; channel = status & 0x0f; switch (type) { case 0x90: Q_EMIT signalWRKNote(track, time, channel, data1, data2, dur); break; case 0xA0: Q_EMIT signalWRKKeyPress(track, time, channel, data1, data2); break; case 0xB0: Q_EMIT signalWRKCtlChange(track, time, channel, data1, data2); break; case 0xC0: Q_EMIT signalWRKProgram(track, time, channel, data1); break; case 0xD0: Q_EMIT signalWRKChanPress(track, time, channel, data1); break; case 0xE0: value = (data2 << 7) + data1 - 8192; Q_EMIT signalWRKPitchBend(track, time, channel, value); break; case 0xF0: Q_EMIT signalWRKSysexEvent(track, time, data1); break; } } if ((i < events) && atEnd()) { Q_EMIT signalWRKError("Corrupted file"); } Q_EMIT signalWRKStreamEnd(time + dur); } void QWrk::processMeterChunk() { int count = read16bit(); for (int i = 0; i < count; ++i) { readGap(4); int measure = read16bit(); int num = readByte(); int den = pow(2.0, readByte()); readGap(4); Q_EMIT signalWRKTimeSig(measure, num, den); } } void QWrk::processMeterKeyChunk() { int count = read16bit(); for (int i = 0; i < count; ++i) { int measure = read16bit(); int num = readByte(); int den = pow(2.0, readByte()); qint8 alt = readByte(); Q_EMIT signalWRKTimeSig(measure, num, den); Q_EMIT signalWRKKeySig(measure, alt); } } double QWrk::getRealTime(long ticks) const { double division = 1.0 * d->m_division; RecTempo last; last.time = 0; last.tempo = 100.0; last.seconds = 0.0; if (!d->m_tempos.isEmpty()) { foreach(const RecTempo& rec, d->m_tempos) { if (rec.time >= ticks) break; last = rec; } } return last.seconds + (((ticks - last.time) / division) * (60.0 / last.tempo)); } void QWrk::processTempoChunk(int factor) { double division = 1.0 * d->m_division; int count = read16bit(); RecTempo last, next; for (int i = 0; i < count; ++i) { long time = read32bit(); readGap(4); long tempo = read16bit() * factor; readGap(8); next.time = time; next.tempo = tempo / 100.0; next.seconds = 0.0; last.time = 0; last.tempo = next.tempo; last.seconds = 0.0; if (! d->m_tempos.isEmpty()) { foreach(const RecTempo& rec, d->m_tempos) { if (rec.time >= time) break; last = rec; } next.seconds = last.seconds + (((time - last.time) / division) * (60.0 / last.tempo)); } d->m_tempos.append(next); Q_EMIT signalWRKTempo(time, tempo); } } void QWrk::processSysexChunk() { int j; QString name; QByteArray data; int bank = readByte(); int length = read16bit(); bool autosend = (readByte() != 0); int namelen = readByte(); name = readString(namelen); for(j=0; j> 4; bool autosend = ( (b & 0x0f) != 0); int namelen = readByte(); name = readString(namelen); for(j=0; j127 qint8 channel = readByte(); // -1, 0->15 qint8 keyPlus = readByte(); // 0->127 qint8 velPlus = readByte(); // 0->127 qint8 localPort = readByte(); qint8 mode = readByte(); Q_EMIT signalWRKThru(mode, port, channel, keyPlus, velPlus, localPort); } void QWrk::processTrackOffset() { quint16 track = read16bit(); qint16 offset = read16bit(); Q_EMIT signalWRKTrackOffset(track, offset); } void QWrk::processTrackReps() { quint16 track = read16bit(); quint16 reps = read16bit(); Q_EMIT signalWRKTrackReps(track, reps); } void QWrk::processTrackPatch() { quint16 track = read16bit(); qint8 patch = readByte(); Q_EMIT signalWRKTrackPatch(track, patch); } void QWrk::processTimeFormat() { quint16 fmt = read16bit(); quint16 ofs = read16bit(); Q_EMIT signalWRKTimeFormat(fmt, ofs); } void QWrk::processComments() { int len = read16bit(); if (d->m_codec == nullptr) { QByteArray data = readByteArray(len); Q_EMIT signalWRKComments2(data); } else { QString text = readString(len); Q_EMIT signalWRKComments(text); } } void QWrk::processVariableRecord(int max) { int datalen = max - 32; QByteArray data; QString name = readVarString(); readGap(31 - name.length()); for ( int i = 0; i < datalen; ++i ) { data += readByte(); } while (data.endsWith('\0')) { data.chop(1); } Q_EMIT signalWRKVariableRecord(name, data); } void QWrk::processUnknown(int id) { Q_EMIT signalWRKUnknownChunk(id, d->m_lastChunkData); } void QWrk::processNewTrack() { QByteArray data; QString name; qint16 bank = -1; qint16 patch = -1; //qint16 vol = -1; //qint16 pan = -1; qint8 key = -1; qint8 vel = 0; quint8 port = 0; qint8 channel = 0; bool selected = false; bool muted = false; bool loop = false; quint16 track = read16bit(); quint8 len = readByte(); if (d->m_codec == nullptr) { data = readByteArray(len); } else { name = readString(len); } bank = read16bit(); patch = read16bit(); /*vol =*/ read16bit(); /*pan =*/ read16bit(); key = readByte(); vel = readByte(); readGap(7); port = readByte(); channel = readByte(); muted = (readByte() != 0); if (d->m_codec == nullptr) { Q_EMIT signalWRKNewTrack2(data, track, channel, key, vel, port, selected, muted, loop); } else { Q_EMIT signalWRKNewTrack(name, track, channel, key, vel, port, selected, muted, loop); } if (bank > -1) Q_EMIT signalWRKTrackBank(track, bank); if (patch > -1) { if (channel > -1) Q_EMIT signalWRKProgram(track, 0, channel, patch); else Q_EMIT signalWRKTrackPatch(track, patch); } } void QWrk::processSoftVer() { int len = readByte(); QString vers = readString(len); Q_EMIT signalWRKSoftVer(vers); } void QWrk::processTrackName() { int track = read16bit(); int len = readByte(); if (d->m_codec == nullptr) { QByteArray data = readByteArray(len); Q_EMIT signalWRKTrackName2(track, data); } else { QString name = readString(len); Q_EMIT signalWRKTrackName(track, name); } } void QWrk::processStringTable() { if (d->m_codec == nullptr) { QList table; int rows = read16bit(); for (int i = 0; i < rows; ++i) { int len = readByte(); QByteArray name = readByteArray(len); /*int idx =*/ readByte(); table.insert(i, name); } Q_EMIT signalWRKStringTable2(table); } else { QStringList table; int rows = read16bit(); for (int i = 0; i < rows; ++i) { int len = readByte(); QString name = readString(len); /*int idx =*/ readByte(); table.insert(i, name); } Q_EMIT signalWRKStringTable(table); } } void QWrk::processLyricsStream() { quint16 track = read16bit(); int events = read32bit(); processNoteArray(track, events); } void QWrk::processTrackVol() { quint16 track = read16bit(); int vol = read16bit(); Q_EMIT signalWRKTrackVol(track, vol); } void QWrk::processNewTrackOffset() { quint16 track = read16bit(); int offset = read32bit(); Q_EMIT signalWRKTrackOffset(track, offset); } void QWrk::processTrackBank() { quint16 track = read16bit(); int bank = read16bit(); Q_EMIT signalWRKTrackBank(track, bank); } void QWrk::processSegmentChunk() { QString name; QByteArray data; int track = read16bit(); int offset = read32bit(); readGap(8); int len = readByte(); if (d->m_codec == nullptr) { data = readByteArray(len); } else { name = readString(len); } readGap(20); if (d->m_codec == nullptr) { Q_EMIT signalWRKSegment2(track, offset, data); } else { Q_EMIT signalWRKSegment(track, offset, name); } int events = read32bit(); processNoteArray(track, events); } void QWrk::processNewStream() { QString name; QByteArray data; int track = read16bit(); int len = readByte(); if (d->m_codec == nullptr) { data = readByteArray(len); Q_EMIT signalWRKSegment2(track, 0, data); } else { name = readString(len); Q_EMIT signalWRKSegment(track, 0, name); } int events = read32bit(); processNoteArray(track, events); } void QWrk::processEndChunk() { Q_EMIT signalWRKEnd(); } int QWrk::readChunk() { qint64 start_pos = d->internalFilePos(); int ck = readByte(); if (ck != END_CHUNK) { quint32 ck_len = read32bit(); if (ck_len > d->m_IOStream->device()->bytesAvailable()) { Q_EMIT signalWRKError("Corrupted file"); seek(start_pos); return END_CHUNK; } start_pos = d->internalFilePos(); d->m_lastChunkPos = start_pos + ck_len; readRawData(ck_len); seek(start_pos); switch (ck) { case TRACK_CHUNK: processTrackChunk(); break; case VARS_CHUNK: processVarsChunk(); break; case TIMEBASE_CHUNK: processTimebaseChunk(); break; case STREAM_CHUNK: processStreamChunk(); break; case METER_CHUNK: processMeterChunk(); break; case TEMPO_CHUNK: processTempoChunk(100); break; case NTEMPO_CHUNK: processTempoChunk(); break; case SYSEX_CHUNK: processSysexChunk(); break; case THRU_CHUNK: processThruChunk(); break; case TRKOFFS_CHUNK: processTrackOffset(); break; case TRKREPS_CHUNK: processTrackReps(); break; case TRKPATCH_CHUNK: processTrackPatch(); break; case TIMEFMT_CHUNK: processTimeFormat(); break; case COMMENTS_CHUNK: processComments(); break; case VARIABLE_CHUNK: processVariableRecord(ck_len); break; case NTRACK_CHUNK: processNewTrack(); break; case SOFTVER_CHUNK: processSoftVer(); break; case TRKNAME_CHUNK: processTrackName(); break; case STRTAB_CHUNK: processStringTable(); break; case LYRICS_CHUNK: processLyricsStream(); break; case TRKVOL_CHUNK: processTrackVol(); break; case NTRKOFS_CHUNK: processNewTrackOffset(); break; case TRKBANK_CHUNK: processTrackBank(); break; case METERKEY_CHUNK: processMeterKeyChunk(); break; case SYSEX2_CHUNK: processSysex2Chunk(); break; case NSYSEX_CHUNK: processNewSysexChunk(); break; case SGMNT_CHUNK: processSegmentChunk(); break; case NSTREAM_CHUNK: processNewStream(); break; case MARKERS_CHUNK: processMarkers(); break; default: processUnknown(ck); } if (d->internalFilePos() != d->m_lastChunkPos) { //qDebug() << Q_FUNC_INFO << "Current pos:" << d->internalFilePos() << "should be:" << d->m_lastChunkPos; seek(d->m_lastChunkPos); } } return ck; } void QWrk::wrkRead() { QByteArray hdr(HEADER.length(), ' '); d->m_tempos.clear(); d->m_IOStream->device()->read(hdr.data(), HEADER.length()); if (hdr == HEADER) { int vma, vme; int ck_id; readGap(1); vme = readByte(); vma = readByte(); Q_EMIT signalWRKHeader(vma, vme); do { ck_id = readChunk(); } while ((ck_id != END_CHUNK) && !atEnd()); if (!atEnd()) { //qDebug() << Q_FUNC_INFO << "extra junk past the end at" << d->internalFilePos(); readRawData(d->m_IOStream->device()->bytesAvailable()); processUnknown(ck_id); } processEndChunk(); } else Q_EMIT signalWRKError("Invalid file format"); } qint64 QWrk::QWrkPrivate::internalFilePos() { return m_IOStream->device()->pos(); } const QByteArray QWrk::HEADER = QByteArrayLiteral("CAKEWALK"); } // namespace File } // namespace drumstick DISABLE_WARNING_POP drumstick-2.9.0/library/file/rmid.cpp0000644000175000017500000001670614541630232016567 0ustar pedropedro/* Standard RIFF MIDI Component Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) #define right Qt::right #define left Qt::left #define endl Qt::endl #define hex Qt::hex #define dec Qt::dec #endif /** * @file rmid.cpp * Implementation of a class managing RIFF MIDI Files input */ namespace drumstick { namespace File { /** * @addtogroup RMID * @{ * * Rmidi provides a mechanism to parse RIFF RMID Files, without the burden of a policy forcing to use some internal sequence representation. * * RIFF RMID is a wrapper format for MIDI data, as first specified by Microsoft, and later extended by MIDI.org (an arm of the MIDI Manufacturers Association) to permit the bundling of both MIDI files and Downloadable Sounds (DLS) files. According to Multimedia Programming Interface and Data Specifications 1.0, August 1991.: The 'RMID' format consists of a standard MIDI file enclosed in a RIFF chunk. Enclosing the MIDI file in a 'RIFF' chunk allows the file to be consistently identified; for example, an 'INFO' list can be included in the file. * * This implementation does not yet support embedded DLS data. This format is deprecated in favor of Extensible Music Files (XMF). * * This class is not related or based on the ALSA library. To parse the SMF portion of the format, the QSmf class should be used. * * @see https://www.loc.gov/preservation/digital/formats/fdd/fdd000120.shtml * @see http://web.archive.org/web/20110610135604/http://www.midi.org/about-midi/rp29spec(rmid).pdf * @} */ const quint32 CKID_RIFF = 0x46464952; const quint32 CKID_LIST = 0x5453494c; const quint32 CKID_INFO = 0x4f464e49; const quint32 CKID_RMID = 0x44494d52; const quint32 CKID_data = 0x61746164; const quint32 CKID_DISP = 0x50534944; const quint32 CKID_DLS = 0x20534C44; /** * Constructor * @param parent Object owner */ Rmidi::Rmidi(QObject * parent): QObject(parent) { } /** * Destructor */ Rmidi::~Rmidi() { } /** * Reads a stream from a disk file. * @param fileName Name of an existing file. */ void Rmidi::readFromFile(QString fileName) { //qDebug() << Q_FUNC_INFO << fileName; QFile file(m_fileName = fileName); file.open(QIODevice::ReadOnly); QDataStream ds(&file); readFromStream(&ds); file.close(); } /** * Reads a stream. * @param ds Pointer to an existing and opened input stream */ void Rmidi::readFromStream(QDataStream* ds) { //qDebug() << Q_FUNC_INFO; if (ds != nullptr) { m_stream = ds; m_stream->setByteOrder(QDataStream::LittleEndian); read(); } } QString Rmidi::toString(quint32 ckid) { QByteArray data(reinterpret_cast(&ckid), sizeof(quint32)); return QString::fromLatin1(data); } QByteArray Rmidi::readByteArray(int size) { //qDebug() << Q_FUNC_INFO << size; char *buffer = new char[size]; m_stream->readRawData(buffer, size); QByteArray ba(buffer); delete[] buffer; return ba; } void Rmidi::skip(quint32 cktype, int size) { Q_UNUSED(cktype) //qDebug() << Q_FUNC_INFO << toString(cktype) << size; m_stream->skipRawData(size); } quint32 Rmidi::readExpectedChunk(quint32 cktype) { quint32 chunkType, len = 0; *m_stream >> chunkType; if (chunkType == cktype) { *m_stream >> len; if (len % 2) len++; // alignment to even size /*qDebug() << Q_FUNC_INFO << "Expected:" << toString(chunkType) << "(" << hex << chunkType << ")" << "length:" << dec << len;*/ } /*else { qDebug() << Q_FUNC_INFO << "Expected:" << toString(cktype) << "(" << hex << cktype << ")" << "got instead:" << toString(chunkType) << "(" << hex << chunkType << ")"; }*/ return len; } quint32 Rmidi::readChunk(quint32& chunkType) { quint32 len = 0; *m_stream >> chunkType; *m_stream >> len; if (len % 2) len++; // alignment to even size /*qDebug() << Q_FUNC_INFO << "chunkType:" << toString(chunkType) << "(" << hex << chunkType << ")" << "length:" << dec << len;*/ return len; } quint32 Rmidi::readChunkID() { quint32 chunkID; *m_stream >> chunkID; /*qDebug() << Q_FUNC_INFO << "chunkID:" << toString(chunkID) << "(" << hex << chunkID << ")";*/ return chunkID; } void Rmidi::processINFO(int size) { //qDebug() << Q_FUNC_INFO << size; quint32 chunkID = 0; quint32 length = 0; while ((size > 0) && !m_stream->atEnd()) { length = readChunk(chunkID); size -= 8; size -= length; QString cktype = toString(chunkID); QByteArray data = readByteArray(length); Q_EMIT signalRiffInfo(cktype, data); } } void Rmidi::processList(int size) { //qDebug() << Q_FUNC_INFO; quint32 chunkID = 0; if (m_stream->atEnd()) return; chunkID = readChunkID(); size -= 4; switch (chunkID) { case CKID_INFO: processINFO(size); break; default: skip(chunkID, size); } } void Rmidi::processRMID(int size) { //qDebug() << Q_FUNC_INFO << size; quint32 chunkID = 0; int length; while ((size > 0) && !m_stream->atEnd()) { length = readChunk(chunkID); size -= 8; switch (chunkID) { case CKID_data: processData("RMID", length); break; case CKID_LIST: processList(length); break; case CKID_DISP: skip(chunkID, length); break; case CKID_RIFF: processRIFF(length); break; default: skip(chunkID, length); } size -= length; } } void Rmidi::processRIFF(int size) { quint32 chunkID = readChunkID(); quint32 length = size - 4; switch(chunkID) { case CKID_RMID: //qDebug() << "RMID format"; processRMID(length); break; case CKID_DLS: //qDebug() << "DLS format"; if (m_stream->device() != nullptr && m_stream->device()->pos() >= 12) { m_stream->device()->seek(m_stream->device()->pos() - 12); processData("DLS", length + 12); } else { skip(chunkID, length); } break; default: qWarning() << "Unsupported format"; skip(chunkID, length); } } void Rmidi::processData(const QString& dataType, int size) { //qDebug() << Q_FUNC_INFO << size; QByteArray memdata(size, '\0'); m_stream->readRawData(memdata.data(), size); Q_EMIT signalRiffData(dataType, memdata); } void Rmidi::read() { //qDebug() << Q_FUNC_INFO; quint32 length = readExpectedChunk(CKID_RIFF); if (length > 0) { processRIFF(length); } } }} // namespace drumstick::File drumstick-2.9.0/library/include/0000755000175000017500000000000014541630232015622 5ustar pedropedrodrumstick-2.9.0/library/include/drumstick.h0000644000175000017500000000322214541630232017777 0ustar pedropedro/* Drumstick MIDI C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DRUMSTICK_H #define DRUMSTICK_H #include /** * @file drumstick.h * The main header that a program can include to use all drumstick features. */ #if defined(Q_OS_LINUX) // ALSA library interface #include #include #include #include #include #include #include #include #endif // File formats #include #include #include // RealTime interfaces #include #include #include // Widgets #include #include #include #include #endif /*DRUMSTICK_H*/ drumstick-2.9.0/library/include/drumstick/0000755000175000017500000000000014541630232017627 5ustar pedropedrodrumstick-2.9.0/library/include/drumstick/alsaevent.h0000644000175000017500000006032514541630232021770 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DRUMSTICK_ALSAEVENT_H #define DRUMSTICK_ALSAEVENT_H extern "C" { #include } #include #include #include "macros.h" namespace drumstick { namespace ALSA { /** * @file alsaevent.h * Classes managing ALSA Sequencer events. * * @addtogroup ALSAEvent ALSA Sequencer Events * @{ */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_ALSA_EXPORT #else #if defined(drumstick_alsa_EXPORTS) #define DRUMSTICK_ALSA_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_ALSA_EXPORT Q_DECL_IMPORT #endif #endif /** * 8-bit unsigned number to be used as a MIDI message parameter */ typedef quint8 MidiByte; /** * Constant SequencerEventType is the QEvent::type() of any SequencerEvent * object to be used to check the argument in QObject::customEvent(). */ const QEvent::Type SequencerEventType = QEvent::Type(QEvent::User + 4154); // :-) /** * Base class for the event's hierarchy * * All event classes share this base class. It provides several common * properties and methods. */ class DRUMSTICK_ALSA_EXPORT SequencerEvent : public QEvent { public: SequencerEvent(); SequencerEvent(const SequencerEvent& other); explicit SequencerEvent(const snd_seq_event_t* event); SequencerEvent& operator=(const SequencerEvent& other); void setSequencerType(const snd_seq_event_type_t eventType); /** * Gets the sequencer event type. * @return The sequencer event type. * @see setSequencerType() */ snd_seq_event_type_t getSequencerType() const { return m_event.type; } void setDestination(const unsigned char client, const unsigned char port); void setSource(const unsigned char port); /** * Gets the source client id. * @return The source client id. * @see setSource() */ unsigned char getSourceClient() const { return m_event.source.client; } /** * Gets the source port id. * @return The source port id. * @see setSource() */ unsigned char getSourcePort() const { return m_event.source.port; } /** * Gets the tick time of the event. * @return The tick time. * @see scheduleTick() */ snd_seq_tick_time_t getTick() const { return m_event.time.tick; } /** * Gets the seconds of the event's real time. * @return The seconds of the time. * @see scheduleReal(), getRealTimeNanos() */ unsigned int getRealTimeSecs() const { return m_event.time.time.tv_sec; } /** * Gets the nanoseconds of the event's real time. * @return The nanoseconds of the time. * @see scheduleReal(), getRealTimeSecs() */ unsigned int getRealTimeNanos() const { return m_event.time.time.tv_nsec; } void setSubscribers(); void setBroadcast(); void setDirect(); void scheduleTick(const int queue, const int tick, const bool relative); void scheduleReal(const int queue, const ulong secs, const ulong nanos, const bool relative); void setPriority(const bool high); /** * Gets the tag of the event * @return The event's tag * @see setTag() */ unsigned char getTag() const { return m_event.tag; } void setTag(const unsigned char aTag); unsigned int getRaw32(const unsigned int n) const; void setRaw32(const unsigned int n, const unsigned int value); unsigned char getRaw8(const unsigned int n) const; void setRaw8(const unsigned int n, const unsigned char value); /** * Gets the handle of the event * @return The event's handle */ snd_seq_event_t* getHandle() { return &m_event; } int getEncodedLength(); static bool isSubscription(const SequencerEvent* event); static bool isPort(const SequencerEvent* event); static bool isClient(const SequencerEvent* event); static bool isConnectionChange(const SequencerEvent* event); static bool isChannel(const SequencerEvent* event); virtual SequencerEvent* clone() const; protected: Q_DECL_DEPRECATED void free(); /** * ALSA sequencer event record. * @see https://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__event.html */ snd_seq_event_t m_event; }; /** * Base class for the events having a Channel property */ class DRUMSTICK_ALSA_EXPORT ChannelEvent : public SequencerEvent { public: /** Default constructor */ ChannelEvent() : SequencerEvent() {} /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit ChannelEvent(const snd_seq_event_t* event) : SequencerEvent(event) {} /** * Sets the channel of the event * @param c A channel, between 0 and 15. * @see getChannel() */ void setChannel(const MidiByte c) { m_event.data.note.channel = (c & 0xf); } /** * Gets the event's channel * @return The event's channel * @see setChannel() */ int getChannel() const { return m_event.data.note.channel; } virtual ChannelEvent* clone() const override; }; /** * Base class for the events having Key and Velocity properties. */ class DRUMSTICK_ALSA_EXPORT KeyEvent : public ChannelEvent { public: /** Default constructor */ KeyEvent() : ChannelEvent() {} /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit KeyEvent(const snd_seq_event_t* event) : ChannelEvent(event) {} /** * Gets the MIDI note of this event. * @return The event's MIDI note. * @see setKey() */ int getKey() const { return m_event.data.note.note; } /** * Sets the MIDI note of this event. * @param b A MIDI note, between 0 and 127. * @see getKey() */ void setKey(const MidiByte b) { m_event.data.note.note = b; } /** * Gets the note velocity of this event. * @return The event's note velocity. * @see setVelocity() */ int getVelocity() const { return m_event.data.note.velocity; } /** * Sets the note velocity of this event. * @param b A velocity value, between 0 and 127. * @see getVelocity() */ void setVelocity(const MidiByte b) { m_event.data.note.velocity = b; } virtual KeyEvent* clone() const override; }; /** * Class representing a note event with duration * * Note events are converted into two MIDI events, a note-on and a note-off * over the wire. */ class DRUMSTICK_ALSA_EXPORT NoteEvent : public KeyEvent { public: /** Default constructor */ NoteEvent() : KeyEvent() { m_event.type = SND_SEQ_EVENT_NOTE; } /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit NoteEvent(const snd_seq_event_t* event) : KeyEvent(event) {} /** * Constructor */ NoteEvent(const int ch, const int key, const int vel, const int dur); /** * Gets the note's duration * @return The duration of the event * @see setDuration() */ ulong getDuration() const { return m_event.data.note.duration; } /** * Sets the note's duration * @param d The duration of the event * @see getDuration() */ void setDuration(const ulong d) { m_event.data.note.duration = d; } virtual NoteEvent* clone() const override; }; /** * Event representing a note-on MIDI event */ class DRUMSTICK_ALSA_EXPORT NoteOnEvent : public KeyEvent { public: /** Default constructor */ NoteOnEvent() : KeyEvent() { m_event.type = SND_SEQ_EVENT_NOTEON; } /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit NoteOnEvent(const snd_seq_event_t* event) : KeyEvent(event) {} /** * Constructor */ NoteOnEvent(const int ch, const int key, const int vel); virtual NoteOnEvent* clone() const override; }; /** * Event representing a note-off MIDI event */ class DRUMSTICK_ALSA_EXPORT NoteOffEvent : public KeyEvent { public: /** Default constructor */ NoteOffEvent() : KeyEvent() { m_event.type = SND_SEQ_EVENT_NOTEOFF; } /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit NoteOffEvent(const snd_seq_event_t* event) : KeyEvent(event) {} /** * Constructor */ NoteOffEvent(const int ch, const int key, const int vel); virtual NoteOffEvent* clone() const override; }; /** * Event representing a MIDI key pressure, or polyphonic after-touch event */ class DRUMSTICK_ALSA_EXPORT KeyPressEvent : public KeyEvent { public: /** Default constructor */ KeyPressEvent() : KeyEvent() { m_event.type = SND_SEQ_EVENT_KEYPRESS; } /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit KeyPressEvent(const snd_seq_event_t* event) : KeyEvent(event) {} /** * Constructor */ KeyPressEvent(const int ch, const int key, const int vel); virtual KeyPressEvent* clone() const override; }; /** * Event representing a MIDI control change event */ class DRUMSTICK_ALSA_EXPORT ControllerEvent : public ChannelEvent { public: /** Default constructor */ ControllerEvent() : ChannelEvent() {} /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit ControllerEvent(const snd_seq_event_t* event) : ChannelEvent(event) {} /** * Constructor */ ControllerEvent(const int ch, const int cc, const int val); /** * Gets the controller event's parameter. * @return The controller event's parameter. * @see setParam() */ uint getParam() const { return m_event.data.control.param; } /** * Sets the controller event's parameter. * @param p The controller event's parameter. * @see getParam() */ void setParam( const uint p ) { m_event.data.control.param = p; } /** * Gets the controller event's value. * @return The controller event's value. * @see setValue() */ int getValue() const { return m_event.data.control.value; } /** * Sets the controller event's value. * @param v The controller event's value. * @see getValue() */ void setValue( const int v ) { m_event.data.control.value = v; } virtual ControllerEvent* clone() const override; }; /** * Event representing a MIDI program change event */ class DRUMSTICK_ALSA_EXPORT ProgramChangeEvent : public ChannelEvent { public: /** Default constructor */ ProgramChangeEvent() : ChannelEvent() { m_event.type = SND_SEQ_EVENT_PGMCHANGE; } /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit ProgramChangeEvent(const snd_seq_event_t* event) : ChannelEvent(event) {} /** * Constructor */ ProgramChangeEvent(const int ch, const int val); /** * Gets the MIDI program number * @return the MIDI program number */ int getValue() const { return m_event.data.control.value; } /** * Sets the MIDI program number * @param v the MIDI program number */ void setValue( const int v ) { m_event.data.control.value = v; } virtual ProgramChangeEvent* clone() const override; }; /** * Event representing a MIDI bender, or pitch wheel event */ class DRUMSTICK_ALSA_EXPORT PitchBendEvent : public ChannelEvent { public: /** Default constructor */ PitchBendEvent() : ChannelEvent() { m_event.type = SND_SEQ_EVENT_PITCHBEND; } /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit PitchBendEvent(const snd_seq_event_t* event) : ChannelEvent(event) {} /** * Constructor */ PitchBendEvent(const int ch, const int val); /** * Gets the MIDI pitch bend value, zero centered from -8192 to 8191 * @return the MIDI pitch bend value */ int getValue() const { return m_event.data.control.value; } /** * Sets the MIDI pitch bend value, zero centered from -8192 to 8191 * @param v the MIDI pitch bend value */ void setValue( const int v ) { m_event.data.control.value = v; } virtual PitchBendEvent* clone() const override; }; /** * Event representing a MIDI channel pressure or after-touch event */ class DRUMSTICK_ALSA_EXPORT ChanPressEvent : public ChannelEvent { public: /** Default constructor */ ChanPressEvent() : ChannelEvent() { m_event.type = SND_SEQ_EVENT_CHANPRESS; } /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit ChanPressEvent( const snd_seq_event_t* event ) : ChannelEvent(event) {} /** * Constructor */ ChanPressEvent( const int ch, const int val ); /** * Gets the channel aftertouch value * @return the channel aftertouch value */ int getValue() const { return m_event.data.control.value; } /** * Sets the channel aftertouch value * @param v the channel aftertouch value */ void setValue( const int v ) { m_event.data.control.value = v; } virtual ChanPressEvent* clone() const override; }; /** * Base class for variable length events */ class DRUMSTICK_ALSA_EXPORT VariableEvent : public SequencerEvent { public: VariableEvent(); explicit VariableEvent(const snd_seq_event_t* event); explicit VariableEvent(const QByteArray& data); VariableEvent(const VariableEvent& other); VariableEvent(const unsigned int datalen, char* dataptr); VariableEvent& operator=(const VariableEvent& other); /** * Gets the data length * @return the data length */ unsigned int getLength() const { return m_event.data.ext.len; } /** * Gets the data pointer * @return the data pointer */ const char* getData() const { return static_cast(m_event.data.ext.ptr); } virtual VariableEvent* clone() const override; protected: QByteArray m_data; }; /** * Event representing a MIDI system exclusive event */ class DRUMSTICK_ALSA_EXPORT SysExEvent : public VariableEvent { public: SysExEvent(); explicit SysExEvent(const snd_seq_event_t* event); explicit SysExEvent(const QByteArray& data); SysExEvent(const SysExEvent& other); SysExEvent(const unsigned int datalen, char* dataptr); SysExEvent &operator=(const SysExEvent &other); virtual SysExEvent* clone() const override; }; /** * Event representing a SMF text event * * This event type is not intended to be transmitted over the wire to an * external device, but it is useful for sequencer programs or MIDI applications */ class DRUMSTICK_ALSA_EXPORT TextEvent : public VariableEvent { public: TextEvent(); explicit TextEvent(const snd_seq_event_t* event); explicit TextEvent(const QString& text, const int textType = 1); TextEvent(const TextEvent& other); TextEvent(const unsigned int datalen, char* dataptr); TextEvent &operator=(const TextEvent &other); QString getText() const; int getTextType() const; virtual TextEvent* clone() const override; protected: int m_textType; }; /** * Generic event */ class DRUMSTICK_ALSA_EXPORT SystemEvent : public SequencerEvent { public: /** Default constructor */ SystemEvent() : SequencerEvent() {} /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit SystemEvent(const snd_seq_event_t* event) : SequencerEvent(event) {} explicit SystemEvent(const snd_seq_event_type_t type); virtual SystemEvent* clone() const override; }; /** * ALSA Event representing a queue control command * * This event is used to schedule changes to the ALSA queues */ class DRUMSTICK_ALSA_EXPORT QueueControlEvent : public SequencerEvent { public: /** Default constructor */ QueueControlEvent() : SequencerEvent() {} /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit QueueControlEvent(const snd_seq_event_t* event) : SequencerEvent(event) {} QueueControlEvent(const snd_seq_event_type_t type, const int queue, const int value); /** * Gets the queue number * @return the queue number */ int getQueue() const { return m_event.data.queue.queue; } /** * Sets the queue number * @param q the queue number */ void setQueue(const uchar q) { m_event.data.queue.queue = q; } /** * Gets the event's value * @return the event's value */ int getValue() const { return m_event.data.queue.param.value; } /** * Sets the event's value * @param val the event's value */ void setValue(const int val) { m_event.data.queue.param.value = val; } /** * Gets the queue position * @return the queue position */ uint getPosition() const { return m_event.data.queue.param.position; } /** * Sets the queue position * @param pos the queue position */ void setPosition(const uint pos) { m_event.data.queue.param.position = pos; } /** * Gets the musical time in ticks * @return the musical time in ticks */ snd_seq_tick_time_t getTickTime() const { return m_event.data.queue.param.time.tick; } /** * Sets the musical time in ticks * @param t the musical time in ticks */ void setTickTime(const snd_seq_tick_time_t t) { m_event.data.queue.param.time.tick = t; } /** * Gets the skew base * @return the skew base */ uint getSkewBase() const { return m_event.data.queue.param.skew.base; } /** * Sets the skew base, should be 65536 * @param base the skew base, should be 65536 */ void setSkewBase(const uint base) { m_event.data.queue.param.skew.base = base; } /** * Gets the skew value * @return the skew value */ uint getSkewValue() const { return m_event.data.queue.param.skew.value; } /** * Sets the skew value * @param val the skew value */ void setSkewValue(const uint val) {m_event.data.queue.param.skew.value = val; } virtual QueueControlEvent* clone() const override; }; /** * Generic event having a value property */ class DRUMSTICK_ALSA_EXPORT ValueEvent : public SequencerEvent { public: /** Default constructor */ ValueEvent() : SequencerEvent() {} /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit ValueEvent(const snd_seq_event_t* event) : SequencerEvent(event) {} ValueEvent(const snd_seq_event_type_t type, const int val); /** * Gets the event's value * @return the event's value */ int getValue() const { return m_event.data.control.value; } /** * Sets the event's value * @param v the event's value */ void setValue( const int v ) { m_event.data.control.value = v; } virtual ValueEvent* clone() const override; }; /** * ALSA Event representing a tempo change for an ALSA queue */ class DRUMSTICK_ALSA_EXPORT TempoEvent : public QueueControlEvent { public: /** Default constructor */ TempoEvent() : QueueControlEvent() {} /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit TempoEvent(const snd_seq_event_t* event) : QueueControlEvent(event) {} TempoEvent(const int queue, const int tempo); virtual TempoEvent* clone() const override; }; /** * ALSA Event representing a subscription between two ALSA clients and ports */ class DRUMSTICK_ALSA_EXPORT SubscriptionEvent : public SequencerEvent { public: /** Default constructor */ SubscriptionEvent() : SequencerEvent() {} /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit SubscriptionEvent(const snd_seq_event_t* event) : SequencerEvent(event) {} /** * Returns true if the event was a subscribed port * @return whether the event was a subscribed port */ bool subscribed() const { return (m_event.type == SND_SEQ_EVENT_PORT_SUBSCRIBED); } /** * Returns true if the event was an unsubscribed port * @return whether the event was an unsubscribed port */ bool unsubscribed() const { return (m_event.type == SND_SEQ_EVENT_PORT_UNSUBSCRIBED); } /** * Gets the sender client number * @return the sender client number */ int getSenderClient() const { return m_event.data.connect.sender.client; } /** * Gets the sender port number * @return the sender port number */ int getSenderPort() const { return m_event.data.connect.sender.port; } /** * Gets the destination client number * @return the destination client number */ int getDestClient() const { return m_event.data.connect.dest.client; } /** * Gets the destination port number * @return the destination port number */ int getDestPort() const { return m_event.data.connect.dest.port; } virtual SubscriptionEvent* clone() const override; }; /** * ALSA Event representing a change on some ALSA sequencer client on the system */ class DRUMSTICK_ALSA_EXPORT ClientEvent : public SequencerEvent { public: /** Default constructor */ ClientEvent() : SequencerEvent() {} /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit ClientEvent(const snd_seq_event_t* event) : SequencerEvent(event) {} /** * Gets the client number * @return the client number */ int getClient() const { return m_event.data.addr.client; } virtual ClientEvent* clone() const override; }; /** * ALSA Event representing a change on some ALSA sequencer port on the system */ class DRUMSTICK_ALSA_EXPORT PortEvent : public ClientEvent { public: /** Default constructor */ PortEvent() : ClientEvent() {} /** * Constructor from an ALSA event record * @param event an ALSA event record */ explicit PortEvent(const snd_seq_event_t* event) : ClientEvent(event) {} /** * Gets the port number * @return the port number */ int getPort() const { return m_event.data.addr.port; } virtual PortEvent* clone() const override; }; /** * Auxiliary class to remove events from an ALSA queue * @see MidiClient::removeEvents() */ class DRUMSTICK_ALSA_EXPORT RemoveEvents { public: friend class MidiClient; public: RemoveEvents(); RemoveEvents(const RemoveEvents& other); explicit RemoveEvents(snd_seq_remove_events_t* other); virtual ~RemoveEvents(); RemoveEvents* clone(); RemoveEvents& operator=(const RemoveEvents& other); int getSizeOfInfo() const; int getChannel(); unsigned int getCondition(); const snd_seq_addr_t* getDest(); int getEventType(); int getQueue(); int getTag(); const snd_seq_timestamp_t* getTime(); void setChannel(int chan); void setCondition(unsigned int cond); void setDest(const snd_seq_addr_t* dest); void setEventType(int type); void setQueue(int queue); void setTag(int tag); void setTime(const snd_seq_timestamp_t* time); private: snd_seq_remove_events_t* m_Info; }; /** * Auxiliary class to translate between raw MIDI streams and ALSA events */ class DRUMSTICK_ALSA_EXPORT MidiCodec : public QObject { Q_OBJECT public: explicit MidiCodec(int bufsize, QObject* parent = nullptr); ~MidiCodec(); void init(); long decode(unsigned char *buf, long count, const snd_seq_event_t *ev); long encode(const unsigned char *buf, long count, snd_seq_event_t *ev); long encode(int c, snd_seq_event_t *ev); void enableRunningStatus(bool enable); void resetEncoder(); void resetDecoder(); void resizeBuffer(int bufsize); private: snd_midi_event_t* m_Info; }; /** @} */ }} /* namespace drumstick::ALSA */ Q_DECLARE_METATYPE(drumstick::ALSA::SequencerEvent) Q_DECLARE_METATYPE(drumstick::ALSA::SequencerEvent*) #endif //DRUMSTICK_ALSAEVENT_H drumstick-2.9.0/library/include/drumstick/alsaqueue.h0000644000175000017500000001432014541630232021765 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DRUMSTICK_ALSAQUEUE_H #define DRUMSTICK_ALSAQUEUE_H #include extern "C" { #include } #include "macros.h" namespace drumstick { namespace ALSA { /** * @file alsaqueue.h * Classes managing ALSA Sequencer queues */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_ALSA_EXPORT #else #if defined(drumstick_alsa_EXPORTS) #define DRUMSTICK_ALSA_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_ALSA_EXPORT Q_DECL_IMPORT #endif #endif class MidiClient; class TimerId; /** * @addtogroup ALSAQueue ALSA Sequencer Queues * @{ * * @class QueueInfo * Queue information container. * * This class is used to hold some properties about an ALSA queue object. */ class DRUMSTICK_ALSA_EXPORT QueueInfo { friend class MidiQueue; public: QueueInfo(); QueueInfo(const QueueInfo& other); explicit QueueInfo(snd_seq_queue_info_t* other); virtual ~QueueInfo(); QueueInfo* clone(); QueueInfo& operator=(const QueueInfo& other); int getInfoSize() const; int getId(); QString getName(); int getOwner(); bool isLocked(); unsigned int getFlags(); void setName(QString value); void setOwner(int value); void setLocked(bool locked); void setFlags(unsigned int value); private: snd_seq_queue_info_t* m_Info; }; /** * Queue status container. * * This class is used to retrieve some status information from an ALSA queue. */ class DRUMSTICK_ALSA_EXPORT QueueStatus { friend class MidiQueue; public: QueueStatus(); QueueStatus(const QueueStatus& other); explicit QueueStatus(snd_seq_queue_status_t* other); virtual ~QueueStatus(); QueueStatus* clone(); QueueStatus& operator=(const QueueStatus& other); int getInfoSize() const; int getId(); int getEvents(); const snd_seq_real_time_t* getRealtime(); unsigned int getStatusBits(); bool isRunning(); double getClockTime(); snd_seq_tick_time_t getTickTime(); private: snd_seq_queue_status_t* m_Info; }; /** * Queue tempo container. * * This class is used to hold some tempo properties of an ALSA queue object. * The queue's resolution defines the meaning of the musical time, in ticks. It * is expressed in PPQ (parts per quarter), or ticks in a quarter note (crotchet). * The nominal tempo is usually expressed in BPM (beats per minute), or Maelzel * metronome units. It can be also given in microseconds per beat. The tempo skew * factor is given as two integer numbers: skew value and skew base, being the * factor the quotient of both quantities = value / base. Currently (ALSA <= 1.0.20) * you can only use the base constant 0x10000 (decimal 65536). */ class DRUMSTICK_ALSA_EXPORT QueueTempo { friend class MidiQueue; public: QueueTempo(); QueueTempo(const QueueTempo& other); explicit QueueTempo(snd_seq_queue_tempo_t* other); virtual ~QueueTempo(); QueueTempo* clone(); QueueTempo& operator=(const QueueTempo& other); int getInfoSize() const; int getId(); int getPPQ(); unsigned int getSkewValue(); unsigned int getSkewBase(); unsigned int getTempo(); void setPPQ(int value); void setSkewValue(unsigned int value); void setTempo(unsigned int value); float getNominalBPM(); float getRealBPM(); void setTempoFactor(float value); void setNominalBPM(float value); protected: void setSkewBase(unsigned int value); private: snd_seq_queue_tempo_t* m_Info; }; /** * Queue timer container. * * This class is used to hold some properties about the Timer used with an ALSA * queue object. */ class DRUMSTICK_ALSA_EXPORT QueueTimer { friend class MidiQueue; public: QueueTimer(); QueueTimer(const QueueTimer& other); explicit QueueTimer(snd_seq_queue_timer_t* other); virtual ~QueueTimer(); QueueTimer* clone(); QueueTimer& operator=(const QueueTimer& other); int getInfoSize() const; int getQueueId(); snd_seq_queue_timer_type_t getType(); const snd_timer_id_t* getId(); unsigned int getResolution(); void setType(snd_seq_queue_timer_type_t value); void setId(snd_timer_id_t* value); void setId(const TimerId& id); void setResolution(unsigned int value); private: snd_seq_queue_timer_t* m_Info; }; /** * Queue management. * * This class represents an ALSA sequencer queue object. */ class DRUMSTICK_ALSA_EXPORT MidiQueue : public QObject { Q_OBJECT public: explicit MidiQueue(MidiClient* seq, QObject* parent = nullptr); MidiQueue(MidiClient* seq, const QueueInfo& info, QObject* parent = nullptr); MidiQueue(MidiClient* seq, const QString name, QObject* parent = nullptr); MidiQueue(MidiClient* seq, const int queue_id, QObject* parent = nullptr); virtual ~MidiQueue(); int getId() const { return m_Id; } void start(); void stop(); void continueRunning(); void clear(); void setTickPosition(snd_seq_tick_time_t pos); void setRealTimePosition(snd_seq_real_time_t* pos); QueueInfo& getInfo(); QueueStatus& getStatus(); QueueTempo& getTempo(); QueueTimer& getTimer(); int getUsage(); void setInfo(const QueueInfo& value); void setTempo(const QueueTempo& value); void setTimer(const QueueTimer& value); void setUsage(int used); private: bool m_allocated; int m_Id; MidiClient* m_MidiClient; QueueInfo m_Info; QueueTempo m_Tempo; QueueTimer m_Timer; QueueStatus m_Status; }; /** @} */ }} /* namespace drumstick::ALSA */ #endif //DRUMSTICK_ALSAQUEUE_H drumstick-2.9.0/library/include/drumstick/backendmanager.h0000644000175000017500000000776114541630232022735 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef BACKENDMANAGER_H #define BACKENDMANAGER_H #include #include #include "macros.h" #include "rtmidiinput.h" #include "rtmidioutput.h" /** * @file backendmanager.h * BackendManager class declaration */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_RT_EXPORT #else #if defined(drumstick_rt_EXPORTS) #define DRUMSTICK_RT_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_RT_EXPORT Q_DECL_IMPORT #endif #endif namespace drumstick { /** * @ingroup RT * @brief Drumstick Real-Time library */ namespace rt { /** * @addtogroup RT Realtime MIDI (I/O) * @{ */ /** * @brief The BackendManager class manages lists of dynamic and static * backends for applications based on drumstick-rt */ class DRUMSTICK_RT_EXPORT BackendManager { public: /** * @brief BackendManager constructor */ explicit BackendManager(); /** * @brief ~BackendManager destructor */ virtual ~BackendManager(); /** * @brief refresh the list of backends */ void refresh(QSettings *settings = nullptr); /** * @brief refresh the list of backends */ void refresh(const QVariantMap &map); /** * @brief availableInputs * @return list of available MIDI inputs */ QList availableInputs(); /** * @brief availableOutputs * @return list of available MIDI outputs */ QList availableOutputs(); /** * @brief defaultPaths * @return list of paths for backends search */ QStringList defaultPaths(); /** * @brief inputBackendByName * @param name The name of some input backend * @return Input backend instance if available */ MIDIInput *inputBackendByName(const QString name); /** * @brief outputBackendByName * @param name The name of some output backend * @return Output backend instance if available */ MIDIOutput *outputBackendByName(const QString name); /** * @brief findInput returns the backend corresponding * to the provided name, or a suitable input instead. * @param name The name of some input backend * @return Input backend instance if available */ MIDIInput *findInput(QString name); /** * @brief findOutput returns the backend corresponding * to the provided name, or a suitable output instead. * @param name The name of some output backend * @return Output backend instance if available */ MIDIOutput *findOutput(QString name); static const QString QSTR_DRUMSTICK; static const QString QSTR_DRUMSTICK_VERSION; static const QString QSTR_DRUMSTICKRT; static const QString QSTR_DRUMSTICKRT_GROUP; static const QString QSTR_DRUMSTICKRT_PUBLICNAMEIN; static const QString QSTR_DRUMSTICKRT_PUBLICNAMEOUT; static const QString QSTR_DRUMSTICKRT_EXCLUDED; static const QString QSTR_DRUMSTICKRT_PATH; private: class BackendManagerPrivate; QScopedPointer d; }; QString DRUMSTICK_RT_EXPORT drumstickLibraryVersion(); /** @} */ }} // namespace drumstick::rt #endif // BACKENDMANAGER_H drumstick-2.9.0/library/include/drumstick/macros.h0000644000175000017500000000431614541630232021270 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DRUMSTICK_MACROS_H #define DRUMSTICK_MACROS_H #include /** * @file macros.h */ #if defined(_MSC_VER) #define DISABLE_WARNING_PUSH __pragma(warning( push )) #define DISABLE_WARNING_POP __pragma(warning( pop )) #define DISABLE_WARNING(warningNumber) __pragma(warning( disable : warningNumber )) #define DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER DISABLE_WARNING(4100) #define DISABLE_WARNING_UNREFERENCED_FUNCTION DISABLE_WARNING(4505) #define DISABLE_WARNING_DEPRECATED_DECLARATIONS DISABLE_WARNING(4996) #elif defined(__GNUC__) || defined(__clang__) #define DO_PRAGMA(X) _Pragma(#X) #define DISABLE_WARNING_PUSH DO_PRAGMA(GCC diagnostic push) #define DISABLE_WARNING_POP DO_PRAGMA(GCC diagnostic pop) #define DISABLE_WARNING(warningName) DO_PRAGMA(GCC diagnostic ignored #warningName) #define DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER DISABLE_WARNING(-Wunused-parameter) #define DISABLE_WARNING_UNREFERENCED_FUNCTION DISABLE_WARNING(-Wunused-function) #define DISABLE_WARNING_DEPRECATED_DECLARATIONS DISABLE_WARNING(-Wdeprecated-declarations) #else #define DISABLE_WARNING_PUSH #define DISABLE_WARNING_POP #define DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER #define DISABLE_WARNING_UNREFERENCED_FUNCTION #define DISABLE_WARNING_DEPRECATED_DECLARATIONS #endif #endif /* DRUMSTICK_MACROS_H */ drumstick-2.9.0/library/include/drumstick/pianopalette.h0000644000175000017500000000764014541630232022474 0ustar pedropedro/* MIDI Virtual Piano Keyboard Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #ifndef PIANOPALETTE_H #define PIANOPALETTE_H #include #include #include #include #include "macros.h" /** * @file pianopalette.h * Piano Palette declarations */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_WIDGETS_EXPORT #else #if defined(drumstick_widgets_EXPORTS) #define DRUMSTICK_WIDGETS_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_WIDGETS_EXPORT Q_DECL_IMPORT #endif #endif namespace drumstick { namespace widgets { /** * @addtogroup Widgets * @{ * * @enum PalettePolicy * @brief The PalettePolicy enumeration. * * This enum describes the different kind of color palettes supported, which * can be used for highlight, background or foreground colors. */ enum PalettePolicy { PAL_SINGLE = 0, ///< Single highlihgting color for all keys PAL_DOUBLE = 1, ///< Two highlihgting colors (naturals/alterations) PAL_CHANNELS = 2, ///< Different highlihgting colors for each channel PAL_SCALE = 3, ///< Background colors for each chromatic scale note PAL_KEYS = 4, ///< Two background colors (naturals/alterations) PAL_FONT = 5, ///< Foreground font colors for names PAL_HISCALE = 6 ///< Highlighting colors for each chromatic scale note }; /** * @brief The PianoPalette class * * PianoPalette represents a set of colors used to paint the PianoKeybd widgets */ class DRUMSTICK_WIDGETS_EXPORT PianoPalette { Q_GADGET Q_DECLARE_TR_FUNCTIONS(PianoPalette) Q_ENUM(PalettePolicy) public: explicit PianoPalette(const int id); virtual ~PianoPalette() = default; void resetColors(); void retranslateStrings(); int paletteId() const; int getNumColors() const; bool isHighLight() const; bool isBackground() const; bool isForeground() const; QString paletteName() const; void setPaletteName(const QString& name); QString paletteText() const; void setPaletteText(const QString& text); QColor getColor(const int i) const; void setColor(const int n, const QString& s, const QColor& c); void setColor(const int n, const QColor& c); QString getColorName(const int i) const; void setColorName(const int n, const QString& s); void saveColors() const; void loadColors(); bool operator==(const PianoPalette& other) const; bool operator!=(const PianoPalette& other) const; static const QString QSTR_PALETTEPREFIX; friend QDataStream &operator<<(QDataStream& stream, const PianoPalette& palette); friend QDataStream &operator>>(QDataStream& stream, PianoPalette& palette); protected: void initialize(); void resetPaletteSingle(); void resetPaletteDouble(); void resetPaletteChannels(); void resetPaletteScale(); void resetPaletteKeys(); void resetPaletteFont(); void retranslatePaletteSingle(); void retranslatePaletteDouble(); void retranslatePaletteChannels(); void retranslatePaletteScale(); void retranslatePaletteKeys(); void retranslatePaletteFont(); int m_paletteId; QList m_colors; QList m_names; QString m_paletteName; QString m_paletteText; }; /** @} */ }} // namespace drumstick::widgets #endif // PIANOPALETTE_H drumstick-2.9.0/library/include/drumstick/qwrk.h0000644000175000017500000005257514541630232021002 0ustar pedropedro/* WRK File component Copyright (C) 2010-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DRUMSTICK_QWRK_H #define DRUMSTICK_QWRK_H #include "macros.h" #include #include class QTextCodec; class QDataStream; /** * @file qwrk.h * Cakewalk WRK Files Input */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_FILE_EXPORT #else #if defined(drumstick_file_EXPORTS) #define DRUMSTICK_FILE_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_FILE_EXPORT Q_DECL_IMPORT #endif #endif namespace drumstick { namespace File { /** * @addtogroup WRK Cakewalk WRK File Parser (Input) * @{ * * @enum WrkChunkType * Record types within a WRK file */ enum WrkChunkType { TRACK_CHUNK = 1, ///< Track prefix STREAM_CHUNK = 2, ///< Events stream VARS_CHUNK = 3, ///< Global variables TEMPO_CHUNK = 4, ///< Tempo map METER_CHUNK = 5, ///< Meter map SYSEX_CHUNK = 6, ///< System exclusive bank MEMRGN_CHUNK = 7, ///< Memory region COMMENTS_CHUNK = 8, ///< Comments TRKOFFS_CHUNK = 9, ///< Track offset TIMEBASE_CHUNK = 10, ///< Timebase. If present is the first chunk in the file. TIMEFMT_CHUNK = 11, ///< SMPTE time format TRKREPS_CHUNK = 12, ///< Track repetitions TRKPATCH_CHUNK = 14, ///< Track patch NTEMPO_CHUNK = 15, ///< New Tempo map THRU_CHUNK = 16, ///< Extended thru parameters LYRICS_CHUNK = 18, ///< Events stream with lyrics TRKVOL_CHUNK = 19, ///< Track volume SYSEX2_CHUNK = 20, ///< System exclusive bank MARKERS_CHUNK = 21, ///< Markers STRTAB_CHUNK = 22, ///< Table of text event types METERKEY_CHUNK = 23, ///< Meter/Key map TRKNAME_CHUNK = 24, ///< Track name VARIABLE_CHUNK = 26, ///< Variable record chunk NTRKOFS_CHUNK = 27, ///< Track offset TRKBANK_CHUNK = 30, ///< Track bank NTRACK_CHUNK = 36, ///< Track prefix NSYSEX_CHUNK = 44, ///< System exclusive bank NSTREAM_CHUNK = 45, ///< Events stream SGMNT_CHUNK = 49, ///< Segment prefix SOFTVER_CHUNK = 74, ///< Software version which saved the file END_CHUNK = 255 ///< Last chunk, end of file }; /** * Cakewalk WRK file format (input only) * * This class is used to parse Cakewalk WRK Files. * Signals with QString parameters are deprecated because the class QTextCodec was removed from QtCore since Qt6. * * @since 0.3.0 */ class DRUMSTICK_FILE_EXPORT QWrk : public QObject { Q_OBJECT Q_ENUM(WrkChunkType) public: explicit QWrk(QObject * parent = nullptr); virtual ~QWrk(); void readFromStream(QDataStream *stream); void readFromFile(const QString& fileName); Q_DECL_DEPRECATED QTextCodec* getTextCodec(); Q_DECL_DEPRECATED void setTextCodec(QTextCodec *codec); long getFilePos(); int getNow() const; int getFrom() const; int getThru() const; int getKeySig() const; int getClock() const; int getAutoSave() const; int getPlayDelay() const; bool getZeroCtrls() const; bool getSendSPP() const; bool getSendCont() const; bool getPatchSearch() const; bool getAutoStop() const; unsigned int getStopTime() const; bool getAutoRewind() const; int getRewindTime() const; bool getMetroPlay() const; bool getMetroRecord() const; bool getMetroAccent() const; int getCountIn() const; bool getThruOn() const; bool getAutoRestart() const; int getCurTempoOfs() const; int getTempoOfs1() const; int getTempoOfs2() const; int getTempoOfs3() const; bool getPunchEnabled() const; int getPunchInTime() const; int getPunchOutTime() const; int getEndAllTime() const; QByteArray getLastChunkRawData() const; double getRealTime(long ticks) const; /** * Cakewalk WRK file format header string id */ static const QByteArray HEADER; ///< Cakewalk WRK File header id Q_SIGNALS: /** * Emitted for a WRK file read error * * @param errorStr Error string */ void signalWRKError(const QString& errorStr); /** * Emitted after reading an unknown chunk * * @param type chunk type * @param data chunk data (not decoded) */ void signalWRKUnknownChunk(int type, const QByteArray& data); /** * Emitted after reading a WRK header * * @param verh WRK file format version major * @param verl WRK file format version minor */ void signalWRKHeader(int verh, int verl); /** * Emitted after reading the last chunk of a WRK file */ void signalWRKEnd(); /** * Emitted after reading the last event of a event stream * @param time musical time */ void signalWRKStreamEnd(long time); /** * Emitted after reading a Note message * * @param track track number * @param time musical time * @param chan MIDI Channel * @param pitch MIDI Note * @param vol Velocity * @param dur Duration */ void signalWRKNote(int track, long time, int chan, int pitch, int vol, int dur); /** * Emitted after reading a Polyphonic Aftertouch message * * @param track track number * @param time musical time * @param chan MIDI Channel * @param pitch MIDI Note * @param press Pressure amount */ void signalWRKKeyPress(int track, long time, int chan, int pitch, int press); /** * Emitted after reading a Control Change message * * @param track track number * @param time musical time * @param chan MIDI Channel * @param ctl MIDI Controller * @param value Control value */ void signalWRKCtlChange(int track, long time, int chan, int ctl, int value); /** * Emitted after reading a Bender message * * @param track track number * @param time musical time * @param chan MIDI Channel * @param value Bender value */ void signalWRKPitchBend(int track, long time, int chan, int value); /** * Emitted after reading a Program change message * * @param track track number * @param time musical time * @param chan MIDI Channel * @param patch Program number */ void signalWRKProgram(int track, long time, int chan, int patch); /** * Emitted after reading a Channel Aftertouch message * * @param track track number * @param time musical time * @param chan MIDI Channel * @param press Pressure amount */ void signalWRKChanPress(int track, long time, int chan, int press); /** * Emitted after reading a System Exclusive event * * @param track track number * @param time musical time * @param bank Sysex Bank number */ void signalWRKSysexEvent(int track, long time, int bank); /** * Emitted after reading a System Exclusive Bank * * @param bank Sysex Bank number * @param name Sysex Bank name * @param autosend Send automatically after loading the song * @param port MIDI output port * @param data Sysex bytes */ void signalWRKSysex(int bank, const QString& name, bool autosend, int port, const QByteArray& data); /** * Emitted after reading a text message * * @param track track number * @param time musical time * @param type Text type * @param data Text data * @deprecated because the class QTextCodec was removed from QtCore since Qt6. * use signalWRKText2() instead */ Q_DECL_DEPRECATED void signalWRKText(int track, long time, int type, const QString& data); /** * Emitted after reading a WRK Time signature * * @param bar Measure number * @param num Numerator * @param den Denominator (exponent in a power of two) */ void signalWRKTimeSig(int bar, int num, int den); /** * Emitted after reading a WRK Key Signature * * @param bar Measure number * @param alt Number of alterations (negative=flats, positive=sharps) */ void signalWRKKeySig(int bar, int alt); /** * Emitted after reading a Tempo Change message. * * Tempo units are given in beats * 100 per minute, so to obtain BPM * it is necessary to divide by 100 the tempo. * * @param time musical time * @param tempo beats per minute multiplied by 100 */ void signalWRKTempo(long time, int tempo); /** * Emitted after reading a track prefix chunk * * @param name1 track 1st name * @param name2 track 2nd name * @param trackno track number * @param channel track forced channel (-1=no forced) * @param pitch track pitch transpose in semitones (-127..127) * @param velocity track velocity increment (-127..127) * @param port track forced port * @param selected true if track is selected * @param muted true if track is muted * @param loop true if loop is enabled * @deprecated because the class QTextCodec was removed from QtCore since Qt6. * use signalWRKTrack2() instead */ Q_DECL_DEPRECATED void signalWRKTrack(const QString& name1, const QString& name2, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop ); /** * Emitted after reading the timebase chunk * * @param timebase ticks per quarter note */ void signalWRKTimeBase(int timebase); /** * Emitted after reading the global variables chunk. * * This record contains miscellaneous Cakewalk global variables that can * be retrieved using individual getters. * * @see getNow(), getFrom(), getThru() */ void signalWRKGlobalVars(); /** * Emitted after reading an Extended Thru parameters chunk. * * It was introduced in Cakewalk version 4.0. These parameters are * intended to override the global vars Thruon value, so this record should * come after the VARS_CHUNK record. It is optional. * * @param mode (auto, off, on) * @param port MIDI port * @param channel MIDI channel * @param keyPlus Note transpose * @param velPlus Velocity transpose * @param localPort MIDI local port */ void signalWRKThru(int mode, int port, int channel, int keyPlus, int velPlus, int localPort); /** * Emitted after reading a track offset chunk * * @param track track number * @param offset time offset */ void signalWRKTrackOffset(int track, int offset); /** * Emitted after reading a track offset chunk * * @param track track number * @param reps number of repetitions */ void signalWRKTrackReps(int track, int reps); /** * Emitted after reading a track patch chunk * * @param track track number * @param patch */ void signalWRKTrackPatch(int track, int patch); /** * Emitted after reading a track bank chunk * * @param track track number * @param bank */ void signalWRKTrackBank(int track, int bank); /** * Emitted after reading a SMPTE time format chunk * * @param frames frames/sec (24, 25, 29=30-drop, 30) * @param offset frames of offset */ void signalWRKTimeFormat(int frames, int offset); /** * Emitted after reading a comments chunk * * @param data file text comments * @deprecated because the class QTextCodec was removed from QtCore since Qt6. * use signalWRKComments2() instead */ Q_DECL_DEPRECATED void signalWRKComments(const QString& data); /** * Emitted after reading a variable chunk. * This record may contain data in text or binary format. * * @param name record identifier * @param data record variable data */ void signalWRKVariableRecord(const QString& name, const QByteArray& data); /** * Emitted after reading a track volume chunk. * * @param track track number * @param vol initial volume */ void signalWRKTrackVol(int track, int vol); /** * Emitted after reading a new track prefix * * @param name track name * @param trackno track number * @param channel forced MIDI channel * @param pitch Note transposition * @param velocity Velocity increment * @param port MIDI port number * @param selected track is selected * @param muted track is muted * @param loop track loop enabled * @deprecated because the class QTextCodec was removed from QtCore since Qt6. * use signalWRKNewTrack2() instead */ Q_DECL_DEPRECATED void signalWRKNewTrack( const QString& name, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop ); /** * Emitted after reading a software version chunk. * * @param version software version string */ void signalWRKSoftVer(const QString& version); /** * Emitted after reading a track name chunk. * * @param track track number * @param name track name * @deprecated because the class QTextCodec was removed from QtCore since Qt6. * use signalWRKTrackName2() instead */ Q_DECL_DEPRECATED void signalWRKTrackName(int track, const QString& name); /** * Emitted after reading a string event types chunk. * * @param strs list of declared string event types * @deprecated because the class QTextCodec was removed from QtCore since Qt6. * use signalWRKStringTable2() instead */ Q_DECL_DEPRECATED void signalWRKStringTable(const QStringList& strs); /** * Emitted after reading a segment prefix chunk. * * @param track track number * @param time segment time offset * @param name segment name * @deprecated because the class QTextCodec was removed from QtCore since Qt6. * use signalWRKSegment2() instead */ Q_DECL_DEPRECATED void signalWRKSegment(int track, long time, const QString& name); /** * Emitted after reading a chord diagram chunk. * * @param track track number * @param time event time in ticks * @param name chord name * @param data chord data definition (not decoded) */ void signalWRKChord(int track, long time, const QString& name, const QByteArray& data); /** * Emitted after reading an expression indication (notation) chunk. * * @param track track number * @param time event time in ticks * @param code expression event code * @param text expression text * @deprecated because the class QTextCodec was removed from QtCore since Qt6. * use signalWRKExpression2() instead */ Q_DECL_DEPRECATED void signalWRKExpression(int track, long time, int code, const QString& text); /** * Emitted after reading a hairpin symbol (notation) chunk. * * @param track track number * @param time event time in ticks * @param code hairpin code * @param dur duration */ void signalWRKHairpin(int track, long time, int code, int dur); /** * Emitted after reading a text message * This signal is emitted when getTextCodec() is nullptr * * @param track track number * @param time musical time * @param type Text type * @param data Text data */ void signalWRKText2(int track, long time, int type, const QByteArray& data); /** * Emitted after reading a track prefix chunk * This signal is emitted when getTextCodec() is nullptr * * @param name1 track 1st name * @param name2 track 2nd name * @param trackno track number * @param channel track forced channel (-1=no forced) * @param pitch track pitch transpose in semitones (-127..127) * @param velocity track velocity increment (-127..127) * @param port track forced port * @param selected true if track is selected * @param muted true if track is muted * @param loop true if loop is enabled */ void signalWRKTrack2(const QByteArray& name1, const QByteArray& name2, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop ); /** * Emitted after reading a comments chunk * This signal is emitted when getTextCodec() is nullptr * * @param data file text comments */ void signalWRKComments2(const QByteArray& data); /** * Emitted after reading a new track prefix * This signal is emitted when getTextCodec() is nullptr * * @param name track name * @param trackno track number * @param channel forced MIDI channel * @param pitch Note transposition * @param velocity Velocity increment * @param port MIDI port number * @param selected track is selected * @param muted track is muted * @param loop track loop enabled */ void signalWRKNewTrack2(const QByteArray& name, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop ); /** * Emitted after reading a track name chunk. * This signal is emitted when getTextCodec() is nullptr * * @param track track number * @param name track name */ void signalWRKTrackName2(int track, const QByteArray& name); /** * Emitted after reading a string event types chunk. * This signal is emitted when getTextCodec() is nullptr * * @param strs list of declared string event types */ void signalWRKStringTable2(const QList& strs); /** * Emitted after reading a segment prefix chunk. * This signal is emitted when getTextCodec() is nullptr * * @param track track number * @param time segment time offset * @param name segment name */ void signalWRKSegment2(int track, long time, const QByteArray& name); /** * Emitted after reading an expression indication (notation) chunk. * This signal is emitted when getTextCodec() is nullptr * * @param track track number * @param time event time in ticks * @param code expression event code * @param text expression text */ void signalWRKExpression2(int track, long time, int code, const QByteArray& text); /** * Emitted after reading a text marker * This is deprecated because the class QTextCodec was removed from QtCore since Qt6 * Use signalWRKMarker2() instead * * @param time event time in ticks or smpte * @param type tipe of time: 0=ticks or 1=smpte * @param data marker text * @deprecated because the class QTextCodec was removed from QtCore since Qt6. * use signalWRKMarker2() instead */ Q_DECL_DEPRECATED void signalWRKMarker(long time, int type, const QString& data); /** * Emitted after reading a text marker * This signal is emitted when getTextCodec() is nullptr * * @param time event time in ticks or smpte * @param type tipe of time: 0=ticks or 1=smpte * @param data marker text */ void signalWRKMarker2(long time, int type, const QByteArray& data); private: quint8 readByte(); quint16 to16bit(quint8 c1, quint8 c2); quint32 to32bit(quint8 c1, quint8 c2, quint8 c3, quint8 c4); quint16 read16bit(); quint32 read24bit(); quint32 read32bit(); QString readString(int len); QString readVarString(); void readRawData(int size); void readGap(int size); bool atEnd(); void seek(qint64 pos); int readChunk(); void processTrackChunk(); void processVarsChunk(); void processTimebaseChunk(); void processNoteArray(int track, int events); void processStreamChunk(); void processMeterChunk(); void processTempoChunk(int factor = 1); void processSysexChunk(); void processSysex2Chunk(); void processNewSysexChunk(); void processThruChunk(); void processTrackOffset(); void processTrackReps(); void processTrackPatch(); void processTrackBank(); void processTimeFormat(); void processComments(); void processVariableRecord(int max); void processNewTrack(); void processSoftVer(); void processTrackName(); void processStringTable(); void processLyricsStream(); void processTrackVol(); void processNewTrackOffset(); void processMeterKeyChunk(); void processSegmentChunk(); void processNewStream(); void processUnknown(int id); void processEndChunk(); void wrkRead(); QByteArray readByteArray(int len); QByteArray readVarByteArray(); void processMarkers(); struct RecTempo { long time; double tempo; double seconds; }; class QWrkPrivate; QScopedPointer d; }; /** @} */ }} // namespace drumstick::File #endif // DRUMSTICK_QWRK_H drumstick-2.9.0/library/include/drumstick/rtmidiinput.h0000644000175000017500000001232014541630232022346 0ustar pedropedro/* Drumstick MIDI realtime input-output Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef MIDIINPUT_H #define MIDIINPUT_H #include #include #include #include #include #include "macros.h" #include "rtmidioutput.h" /** * @file rtmidiinput.h * Realtime MIDI input interface */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_RT_EXPORT #else #if defined(drumstick_rt_EXPORTS) #define DRUMSTICK_RT_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_RT_EXPORT Q_DECL_IMPORT #endif #endif namespace drumstick { namespace rt { /** * @addtogroup RT * @{ * * @class MIDIInput * @brief MIDI IN interface */ class DRUMSTICK_RT_EXPORT MIDIInput : public QObject { Q_OBJECT public: /** * @brief MIDIInput constructor * @param parent */ explicit MIDIInput(QObject *parent = nullptr) : QObject(parent) {} /** * @brief ~MIDIInput destructor */ virtual ~MIDIInput() = default; /** * @brief initialize * @param settings */ virtual void initialize(QSettings *settings) = 0; /** * @brief backendName * @return plugin name */ virtual QString backendName() = 0; /** * @brief publicName * @return MIDI port name */ virtual QString publicName() = 0; /** * @brief setPublicName * @param name MIDI port name */ virtual void setPublicName(QString name) = 0; /** * @brief connections * @param advanced whether the advanced connections are included or not * @return list of available MIDI ports */ virtual QList connections(bool advanced = false) = 0; /** * @brief setExcludedConnections * @param conns */ virtual void setExcludedConnections(QStringList conns) = 0; /** * @brief open the MIDI port by name * @param conn Connection to open */ virtual void open(const MIDIConnection &conn) = 0; /** * @brief close the MIDI port */ virtual void close() = 0; /** * @brief currentConnection * @return name of the current connection if it is opened */ virtual MIDIConnection currentConnection() = 0; /** * @brief setMIDIThruDevice * @param device */ virtual void setMIDIThruDevice(MIDIOutput *device) = 0; /** * @brief enableMIDIThru * @param enable */ virtual void enableMIDIThru(bool enable) = 0; /** * @brief isEnabledMIDIThru * @return MIDI Thru is enabled */ virtual bool isEnabledMIDIThru() = 0; Q_SIGNALS: /** * @brief midiNoteOff 0x8 * @param chan * @param note * @param vel */ void midiNoteOff(const int chan, const int note, const int vel); /** * @brief midiNoteOn 0x9 * @param chan * @param note * @param vel */ void midiNoteOn(const int chan, const int note, const int vel); /** * @brief midiKeyPressure 0xA * @param chan * @param note * @param value */ void midiKeyPressure(const int chan, const int note, const int value); /** * @brief midiController 0xB * @param chan * @param control * @param value */ void midiController(const int chan, const int control, const int value); /** * @brief midiProgram 0xC * @param chan * @param program */ void midiProgram(const int chan, const int program); /** * @brief midiChannelPressure 0xD * @param chan * @param value */ void midiChannelPressure(const int chan, const int value); /** * @brief midiPitchBend 0xE * @param chan * @param value */ void midiPitchBend(const int chan, const int value); /** * @brief midiSysex * @param data 0xF0 ... 0xF7 */ void midiSysex(const QByteArray &data); /** * @brief midiSystemCommon * @param status 0xF (1..6) */ void midiSystemCommon(const int status); /** * @brief midiSystemRealtime * @param status 0xF (8..F) */ void midiSystemRealtime(const int status); }; /** @} */ }} // namespace drumstick::rt Q_DECLARE_INTERFACE(drumstick::rt::MIDIInput, "net.sourceforge.drumstick.rt.MIDIInput/2.0") #endif // MIDIINPUT_H drumstick-2.9.0/library/include/drumstick/rtmidioutput.h0000644000175000017500000002127214541630232022555 0ustar pedropedro/* Drumstick MIDI realtime input-output Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef MIDIOUTPUT_H #define MIDIOUTPUT_H #include #include #include #include #include #include "macros.h" /** * @file rtmidioutput.h * Realtime MIDI output interface */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_RT_EXPORT #else #if defined(drumstick_rt_EXPORTS) #define DRUMSTICK_RT_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_RT_EXPORT Q_DECL_IMPORT #endif #endif namespace drumstick { namespace rt { /** * @addtogroup RT * @{ */ const quint8 MIDI_STD_CHANNELS = 16; ///< Standard number of MIDI channels const quint8 MIDI_GM_STD_DRUM_CHANNEL = (10-1); ///< Number of the GM percussion channel const quint8 MIDI_CONTROL_MSB_BANK_SELECT = 0x00; ///< MIDI Controller number for MSB Bank number const quint8 MIDI_CONTROL_MSB_MAIN_VOLUME = 0x07; ///< MIDI Controller number for MSB volume const quint8 MIDI_CONTROL_LSB_BANK_SELECT = 0x20; ///< MIDI Controller number for LSB Bank number const quint8 MIDI_CONTROL_REVERB_SEND = 0x5b; ///< MIDI Controller number for Reverb send const quint8 MIDI_CONTROL_ALL_SOUNDS_OFF = 0x78; ///< MIDI Controller number for All sounds off const quint8 MIDI_CONTROL_ALL_NOTES_OFF = 0x7b; ///< MIDI Controller number for All notes off const quint8 MIDI_CONTROL_RESET_CONTROLLERS = 0x79; ///< MIDI Controller number for Reset all controllers const quint8 MIDI_STATUS_NOTEOFF = 0x80; ///< MIDI status byte for NOTE OFF messages const quint8 MIDI_STATUS_NOTEON = 0x90; ///< MIDI status byte for NOTE ON messages const quint8 MIDI_STATUS_KEYPRESURE = 0xa0; ///< MIDI status byte for KEY pressure messages const quint8 MIDI_STATUS_CONTROLCHANGE = 0xb0; ///< MIDI status byte for CONTROL change messages const quint8 MIDI_STATUS_PROGRAMCHANGE = 0xc0; ///< MIDI status byte for PROGRAM change messages const quint8 MIDI_STATUS_CHANNELPRESSURE = 0xd0; ///< MIDI status byte for CHANNEL PRESSURE messages const quint8 MIDI_STATUS_PITCHBEND = 0xe0; ///< MIDI status byte for PITCH bend messages const quint8 MIDI_STATUS_SYSEX = 0xf0; ///< MIDI status byte for System Exclusive START messages const quint8 MIDI_STATUS_ENDSYSEX = 0xf7; ///< MIDI status byte for System Exclusive END messages const quint8 MIDI_STATUS_REALTIME = 0xf8; ///< Minimum value for MIDI Realtime messages status const quint8 MIDI_STATUS_MASK = 0xf0; ///< Mask to extract the MIDI status byte from a MIDI message const quint8 MIDI_CHANNEL_MASK = 0x0f; ///< Mask to extract the MIDI channel byte from a MIDI message const quint8 MIDI_COMMON_QTRFRAME = 0xF1; ///< MIDI Quarter frame status message const quint8 MIDI_COMMON_SONGPP = 0xF2; ///< MIDI Song Position status message const quint8 MIDI_COMMON_SONGSELECT = 0xF3; ///< MIDI Song Select status message const quint8 MIDI_COMMON_TUNEREQ = 0xF6; ///< MIDI Tune Request status message const quint8 MIDI_REALTIME_CLOCK = 0xF8; ///< MIDI Clock status message const quint8 MIDI_REALTIME_START = 0xFA; ///< MIDI Start status message const quint8 MIDI_REALTIME_CONTINUE = 0xFB; ///< MIDI Continue status message const quint8 MIDI_REALTIME_STOP = 0xFC; ///< MIDI Stop status message const quint8 MIDI_REALTIME_SENSING = 0xFE; ///< MIDI Active Sensing status message const quint8 MIDI_REALTIME_RESET = 0xFF; ///< MIDI Reset status message /** * @brief MIDI_LSB is a function to extract the least significative byte of a MIDI value * @param x a MIDI integer value * @return the least significative byte of the input value */ inline int MIDI_LSB(int x) { return (x % 0x80); } /** * @brief MIDI_MSB is a function to extract the most significative byte of a MIDI value * @param x MIDI integer value * @return the most significative byte of the input value */ inline int MIDI_MSB(int x) { return (x / 0x80); } /** * @brief MIDIConnection represents a connection identifier * * MIDIConnection is an alias for QPair where the * first component is a QString representing the symbolic name of the MIDI Port * and the second component is a QVariant that represents the native identification * of the MIDI port, which may be a string, a number, or any other data type * accepted as a QVariant. */ typedef QPair MIDIConnection; /** * @brief MIDI OUT interface */ class DRUMSTICK_RT_EXPORT MIDIOutput : public QObject { Q_OBJECT public: /** * @brief MIDIOutput constructor * @param parent */ explicit MIDIOutput(QObject *parent = nullptr) : QObject(parent) {} /** * @brief ~MIDIOutput destructor */ virtual ~MIDIOutput() = default; /** * @brief initialize * @param settings */ virtual void initialize(QSettings* settings) = 0; /** * @brief backendName * @return plugin name */ virtual QString backendName() = 0; /** * @brief publicName * @return MIDI port name */ virtual QString publicName() = 0; /** * @brief setPublicName * @param name MIDI port name */ virtual void setPublicName(QString name) = 0; /** * @brief connections * @param advanced whether the advanced connections are included or not * @return list of available MIDI ports */ virtual QList connections(bool advanced = false) = 0; /** * @brief setExcludedConnections * @param conns */ virtual void setExcludedConnections(QStringList conns) = 0; /** * @brief open the MIDI port by name * @param conn the MIDI connection to be opened */ virtual void open(const MIDIConnection& conn) = 0; /** * @brief close the MIDI port */ virtual void close() = 0; /** * @brief currentConnection * @return name of the current connection if it is opened */ virtual MIDIConnection currentConnection() = 0; public Q_SLOTS: /** * @brief sendNoteOff 0x8 * @param chan * @param note * @param vel */ virtual void sendNoteOff(int chan, int note, int vel) = 0; /** * @brief sendNoteOn 0x9 * @param chan * @param note * @param vel */ virtual void sendNoteOn(int chan, int note, int vel) = 0; /** * @brief sendKeyPressure 0xA * @param chan * @param note * @param value */ virtual void sendKeyPressure(int chan, int note, int value) = 0; /** * @brief sendController 0xB * @param chan * @param control * @param value */ virtual void sendController(int chan, int control, int value) = 0; /** * @brief sendProgram 0xC * @param chan * @param program */ virtual void sendProgram(int chan, int program) = 0; /** * @brief sendChannelPressure 0xD * @param chan * @param value */ virtual void sendChannelPressure(int chan, int value) = 0; /** * @brief sendPitchBend 0xE * @param chan * @param value */ virtual void sendPitchBend(int chan, int value) = 0; /** * @brief sendSysex * @param data 0xF0 ... 0xF7 */ virtual void sendSysex(const QByteArray& data) = 0; /** * @brief sendSystemMsg * @param status 0xF */ virtual void sendSystemMsg(const int status) = 0; }; /** @} */ }} // namespace drumstick::rt Q_DECLARE_INTERFACE(drumstick::rt::MIDIOutput, "net.sourceforge.drumstick.rt.MIDIOutput/2.0") Q_DECLARE_METATYPE(drumstick::rt::MIDIConnection) #endif /* MIDIOUTPUT_H */ drumstick-2.9.0/library/include/drumstick/sequencererror.h0000644000175000017500000000516414541630232023052 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SEQUENCERERROR_H #define SEQUENCERERROR_H #include #include "macros.h" #include /** * @file sequencererror.h * SequencerError Exception class */ /** * @namespace std * C++ Standard Library namespace * * @class std::exception * Provides consistent interface to handle errors. * @see https://en.cppreference.com/w/cpp/error/exception */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_ALSA_EXPORT #else #if defined(drumstick_alsa_EXPORTS) #define DRUMSTICK_ALSA_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_ALSA_EXPORT Q_DECL_IMPORT #endif #endif namespace drumstick { namespace ALSA { /** * @addtogroup ALSAError ALSA Sequencer Exception * @{ * * @class SequencerError * Exception class for ALSA Sequencer errors. * This class is used to report errors from the ALSA sequencer. * * The class SequencerError represents an exception object reported when the * ALSA library returns an error code. It is only used for severe errors. */ class DRUMSTICK_ALSA_EXPORT SequencerError : std::exception { public: /** * Constructor * @param s Error location * @param rc Numeric error code */ SequencerError(QString const& s, int rc); /** * Retrieve a human readable error message * @return human readable error message */ virtual const char* what() const noexcept override; /** * Gets the human readable error message from the error code * @return Error message */ QString qstrError() const; /** * Gets the numeric error code * @return Error code */ int code() const; /** * Gets the location of the error code as provided in the constructor * @return Error location */ const QString& location() const; private: QString m_location; int m_errCode; }; /** @} */ } // namespace ALSA } // namespace drumstick #endif // SEQUENCERERROR_H drumstick-2.9.0/library/include/drumstick/subscription.h0000644000175000017500000000713214541630232022527 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DRUMSTICK_SUBSCRIPTION_H #define DRUMSTICK_SUBSCRIPTION_H extern "C" { #include } #include #include "macros.h" namespace drumstick { namespace ALSA { /** * @file subscription.h * Classes managing ALSA sequencer subscriptions */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_ALSA_EXPORT #else #if defined(drumstick_alsa_EXPORTS) #define DRUMSTICK_ALSA_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_ALSA_EXPORT Q_DECL_IMPORT #endif #endif class MidiClient; /** * @addtogroup ALSASubs ALSA Sequencer Subscriptions * @{ * * @class Subscriber * Subscriber container class. * * This class is used to enumerate the subscribers of a given (root) port. */ class DRUMSTICK_ALSA_EXPORT Subscriber { friend class PortInfo; public: Subscriber(); Subscriber(const Subscriber& other); explicit Subscriber(snd_seq_query_subscribe_t* other); virtual ~Subscriber(); Subscriber* clone(); int getSizeOfInfo() const; int getClient(); int getPort(); const snd_seq_addr_t* getRoot(); snd_seq_query_subs_type_t getType(); int getIndex(); int getNumSubs(); const snd_seq_addr_t* getAddr(); int getQueue(); bool getExclusive(); bool getTimeUpdate(); bool getTimeReal(); void setClient(int client); void setPort(int port); void setRoot(snd_seq_addr_t* addr); void setType(snd_seq_query_subs_type_t type); void setIndex(int index); Subscriber& operator=(const Subscriber& other); private: snd_seq_query_subscribe_t* m_Info; }; /** * Subscription management. * * This class represents a connection between two ports. */ class DRUMSTICK_ALSA_EXPORT Subscription { public: Subscription(); Subscription(const Subscription& other); explicit Subscription(snd_seq_port_subscribe_t* other); explicit Subscription(MidiClient* seq); virtual ~Subscription(); Subscription* clone(); int getSizeOfInfo() const; void setSender(unsigned char client, unsigned char port); void setDest(unsigned char client, unsigned char port); void subscribe(MidiClient* seq); void unsubscribe(MidiClient* seq); const snd_seq_addr_t* getSender(); const snd_seq_addr_t* getDest(); int getQueue(); bool getExclusive(); bool getTimeUpdate(); bool getTimeReal(); void setSender(const snd_seq_addr_t* addr); void setDest(const snd_seq_addr_t* addr); void setQueue(int queue); void setExclusive(bool val); void setTimeUpdate(bool val); void setTimeReal(bool val); Subscription& operator=(const Subscription& other); private: snd_seq_port_subscribe_t* m_Info; }; /** * List of subscriptions */ typedef QList SubscriptionsList; /** * List of subscribers */ typedef QList SubscribersList; /** @} */ }} /* namespace drumstick::ALSA */ #endif //DRUMSTICK_SUBSCRIPTION_H drumstick-2.9.0/library/include/drumstick/configurationdialogs.h0000644000175000017500000000377414541630232024225 0ustar pedropedro/* Drumstick MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef CONFIGURATIONDIALOGS_H #define CONFIGURATIONDIALOGS_H #include #include #include "macros.h" /** * @file configurationdialogs.h * Functions providing configuration dialogs */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_WIDGETS_EXPORT #else #if defined(drumstick_widgets_EXPORTS) #define DRUMSTICK_WIDGETS_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_WIDGETS_EXPORT Q_DECL_IMPORT #endif #endif namespace drumstick { /** * @ingroup Widgets * @brief Drumstick Widgets library * MIDI related widgets and functions. */ namespace widgets { bool DRUMSTICK_WIDGETS_EXPORT inputDriverIsConfigurable(const QString driver); bool DRUMSTICK_WIDGETS_EXPORT outputDriverIsConfigurable(const QString driver); bool DRUMSTICK_WIDGETS_EXPORT configureInputDriver(const QString driver, QWidget *parent = nullptr); bool DRUMSTICK_WIDGETS_EXPORT configureOutputDriver(const QString driver, QWidget *parent = nullptr); void DRUMSTICK_WIDGETS_EXPORT changeSoundFont(const QString driver, const QString fileName, QWidget *parent = nullptr); QString DRUMSTICK_WIDGETS_EXPORT libraryVersion(); }} // namespace drumstick::widgets #endif // CONFIGURATIONDIALOGS_H drumstick-2.9.0/library/include/drumstick/alsaclient.h0000644000175000017500000002403214541630232022120 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DRUMSTICK_ALSACLIENT_H #define DRUMSTICK_ALSACLIENT_H #include #include #include #include #include #include "macros.h" #include "alsaport.h" /** * @file alsaclient.h * Classes managing ALSA Sequencer clients */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_ALSA_EXPORT #else #if defined(drumstick_alsa_EXPORTS) #define DRUMSTICK_ALSA_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_ALSA_EXPORT Q_DECL_IMPORT #endif #endif /** * @brief Drumstick common */ namespace drumstick { /** * @ingroup ALSAGroup * @brief Drumstick ALSA library wrapper */ namespace ALSA { class MidiQueue; class MidiClient; class SequencerEvent; class RemoveEvents; /** * @addtogroup ALSAClient ALSA Sequencer Clients * @{ * * @class ClientInfo * Client information * * This class is used to retrieve, hold and set some data from * sequencer clients, like the name or id. */ class DRUMSTICK_ALSA_EXPORT ClientInfo { friend class MidiClient; public: ClientInfo(); ClientInfo(const ClientInfo &other); explicit ClientInfo(snd_seq_client_info_t *other); ClientInfo(MidiClient* seq, int id); virtual ~ClientInfo(); ClientInfo* clone(); ClientInfo& operator=(const ClientInfo &other); int getSizeOfInfo() const; int getClientId(); snd_seq_client_type_t getClientType(); QString getName(); bool getBroadcastFilter(); bool getErrorBounce(); int getNumPorts(); int getEventLost(); void setClient(int client); void setName(QString name); void setBroadcastFilter(bool val); void setErrorBounce(bool val); PortInfoList getPorts() const; #if SND_LIB_VERSION > 0x010010 void addFilter(int eventType); bool isFiltered(int eventType); void clearFilter(); void removeFilter(int eventType); #endif protected: void readPorts(MidiClient* seq); void freePorts(); Q_DECL_DEPRECATED const unsigned char* getEventFilter(); Q_DECL_DEPRECATED void setEventFilter(unsigned char* filter); private: snd_seq_client_info_t* m_Info; PortInfoList m_Ports; }; /** * List of sequencer client information */ typedef QList ClientInfoList; /** * System information * * This class is used to retrieve and hold some data about the * whole sequencer subsystem. */ class DRUMSTICK_ALSA_EXPORT SystemInfo { friend class MidiClient; public: SystemInfo(); SystemInfo(const SystemInfo& other); explicit SystemInfo(snd_seq_system_info_t* other); explicit SystemInfo(MidiClient* seq); virtual ~SystemInfo(); SystemInfo* clone(); SystemInfo& operator=(const SystemInfo& other); int getSizeOfInfo() const; int getMaxClients(); int getMaxPorts(); int getMaxQueues(); int getMaxChannels(); int getCurrentQueues(); int getCurrentClients(); private: snd_seq_system_info_t* m_Info; }; /** * Sequencer Pool information * * This class is used to get and set the size of the input and output pool * buffers for a sequencer client. */ class DRUMSTICK_ALSA_EXPORT PoolInfo { friend class MidiClient; public: PoolInfo(); PoolInfo(const PoolInfo& other); explicit PoolInfo(snd_seq_client_pool_t* other); explicit PoolInfo(MidiClient* seq); virtual ~PoolInfo(); PoolInfo* clone(); PoolInfo& operator=(const PoolInfo& other); int getSizeOfInfo() const; int getClientId(); int getInputFree(); int getInputPool(); int getOutputFree(); int getOutputPool(); int getOutputRoom(); void setInputPool(int size); void setOutputPool(int size); void setOutputRoom(int size); private: snd_seq_client_pool_t* m_Info; }; /** * Sequencer events handler * * This abstract class is used to define an interface that other class can * implement to receive sequencer events. It is one of the three methods of * delivering events offered by this library. * * @see ALSAClient */ class DRUMSTICK_ALSA_EXPORT SequencerEventHandler { public: /** Destructor */ virtual ~SequencerEventHandler() = default; /** * Callback function to be implemented by the derived class. * It will be invoked by the client to deliver received events to the * registered listener. * * @param ev A pointer to the received SequencerEvent * @see MidiClient::setHandler(), MidiClient::startSequencerInput(), * MidiClient::stopSequencerInput(), MidiClient::doEvents() */ virtual void handleSequencerEvent(SequencerEvent* ev) = 0; }; /** * Client management. * * This class represents an ALSA sequencer client */ class DRUMSTICK_ALSA_EXPORT MidiClient : public QObject { Q_OBJECT public: explicit MidiClient( QObject* parent = nullptr ); virtual ~MidiClient(); void open( const QString deviceName = "default", const int openMode = SND_SEQ_OPEN_DUPLEX, const bool blockMode = false ); void open( snd_config_t* conf, const QString deviceName = "default", const int openMode = SND_SEQ_OPEN_DUPLEX, const bool blockMode = false ); void close(); void startSequencerInput(); void stopSequencerInput(); MidiPort* createPort(); MidiQueue* createQueue(); MidiQueue* createQueue(QString const& name); MidiQueue* getQueue(); MidiQueue* useQueue(int queue_id); MidiQueue* useQueue(const QString& name); MidiQueue* useQueue(MidiQueue* queue); void portAttach(MidiPort* port); void portDetach(MidiPort* port); void detachAllPorts(); void addEventFilter(int evtype); void output(SequencerEvent* ev, bool async = false, int timeout = -1); void outputDirect(SequencerEvent* ev, bool async = false, int timeout = -1); void outputBuffer(SequencerEvent* ev); void drainOutput(bool async = false, int timeout = -1); void synchronizeOutput(); int getClientId(); snd_seq_type_t getSequencerType(); snd_seq_t* getHandle(); bool isOpened(); size_t getOutputBufferSize(); void setOutputBufferSize(size_t newSize); size_t getInputBufferSize(); void setInputBufferSize(size_t newSize); QString getDeviceName(); int getOpenMode(); bool getBlockMode(); void setBlockMode(bool newValue); QString getClientName(); QString getClientName(const int clientId); void setClientName(QString const& newName); bool getBroadcastFilter(); void setBroadcastFilter(bool newValue); bool getErrorBounce(); void setErrorBounce(bool newValue); ClientInfo& getThisClientInfo(); void setThisClientInfo(const ClientInfo& val); MidiPortList getMidiPorts() const; ClientInfoList getAvailableClients(); PortInfoList getAvailableInputs(); PortInfoList getAvailableOutputs(); SystemInfo& getSystemInfo(); QList getAvailableQueues(); PoolInfo& getPoolInfo(); void setPoolInfo(const PoolInfo& info); void setPoolInput(int size); void setPoolOutput(int size); void setPoolOutputRoom(int size); void resetPoolInput(); void resetPoolOutput(); void dropInput(); void dropInputBuffer(); void dropOutput(); void dropOutputBuffer(); void removeEvents(const RemoveEvents* spec); SequencerEvent* extractOutput(); int outputPending(); int inputPending(bool fetch); int getQueueId(const QString& name); void addListener(QObject* listener); void removeListener(QObject* listener); void setEventsEnabled(const bool bEnabled); bool getEventsEnabled() const; void setHandler(SequencerEventHandler* handler); bool parseAddress( const QString& straddr, snd_seq_addr& result ); void setRealTimeInput(bool enabled); bool realTimeInputEnabled(); Q_SIGNALS: /** Signal emitted when an event is received * @param ev pointer to the received event */ void eventReceived(drumstick::ALSA::SequencerEvent* ev); protected: void doEvents(); void applyClientInfo(); void readClients(); void freeClients(); void updateAvailablePorts(); PortInfoList filterPorts(unsigned int filter); /* low level public functions */ const char * _getDeviceName(); int getPollDescriptorsCount(short events); int pollDescriptors(struct pollfd *pfds, unsigned int space, short events); unsigned short pollDescriptorsRevents(struct pollfd *pfds, unsigned int nfds); /* mid level functions */ void _setClientName( const char *name ); int createSimplePort( const char *name, unsigned int caps, unsigned int type ); void deleteSimplePort( int port ); void connectFrom(int myport, int client, int port); void connectTo(int myport, int client, int port); void disconnectFrom(int myport, int client, int port); void disconnectTo(int myport, int client, int port); private: class SequencerInputThread; class MidiClientPrivate; QScopedPointer d; }; #if SND_LIB_VERSION > 0x010004 DRUMSTICK_ALSA_EXPORT QString getRuntimeALSALibraryVersion(); DRUMSTICK_ALSA_EXPORT int getRuntimeALSALibraryNumber(); #endif DRUMSTICK_ALSA_EXPORT QString getRuntimeALSADriverVersion(); DRUMSTICK_ALSA_EXPORT int getRuntimeALSADriverNumber(); DRUMSTICK_ALSA_EXPORT QString getCompiledALSALibraryVersion(); DRUMSTICK_ALSA_EXPORT QString getDrumstickLibraryVersion(); /** @} */ }} /* namespace drumstick::ALSA */ #endif // DRUMSTICK_ALSACLIENT_H drumstick-2.9.0/library/include/drumstick/alsaport.h0000644000175000017500000001457514541630232021641 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DRUMSTICK_ALSAPORT_H #define DRUMSTICK_ALSAPORT_H #include "subscription.h" #include namespace drumstick { namespace ALSA { /** * @file alsaport.h * Classes managing ALSA Sequencer ports. */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_ALSA_EXPORT #else #if defined(drumstick_alsa_EXPORTS) #define DRUMSTICK_ALSA_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_ALSA_EXPORT Q_DECL_IMPORT #endif #endif class MidiClient; /** * @addtogroup ALSAPort ALSA Sequencer Ports * @{ * * @class PortInfo * Port information container */ class DRUMSTICK_ALSA_EXPORT PortInfo { friend class MidiPort; friend class ClientInfo; friend class MidiClient; public: PortInfo(); PortInfo(const PortInfo& other); explicit PortInfo(snd_seq_port_info_t* other); PortInfo(MidiClient* seq, const int client, const int port); PortInfo(MidiClient* seq, const int port); virtual ~PortInfo(); PortInfo* clone(); PortInfo& operator=(const PortInfo& other); int getSizeOfInfo() const; int getClient(); int getPort(); QString getClientName() const; const snd_seq_addr_t* getAddr(); QString getName(); unsigned int getCapability(); unsigned int getType(); int getMidiChannels(); int getMidiVoices(); int getSynthVoices(); int getReadUse(); int getWriteUse(); int getPortSpecified(); void setClient(int client); void setPort(int port); void setAddr(const snd_seq_addr_t* addr); void setName( QString const& name ); void setCapability(unsigned int capability); void setType(unsigned int type); void setMidiChannels(int channels); void setMidiVoices(int voices); void setSynthVoices(int voices); void setPortSpecified(int val); SubscribersList getReadSubscribers() const; SubscribersList getWriteSubscribers() const; bool getTimestamping(); bool getTimestampReal(); int getTimestampQueue(); void setTimestamping(bool value); void setTimestampReal(bool value); void setTimestampQueue(int queueId); protected: void readSubscribers(MidiClient* seq); void freeSubscribers(); void setClientName(QString name); private: snd_seq_port_info_t* m_Info; QString m_ClientName; SubscribersList m_ReadSubscribers; SubscribersList m_WriteSubscribers; }; /** * List of port information objects */ typedef QList PortInfoList; /** * Port management. * * This class represents an ALSA sequencer port. */ class DRUMSTICK_ALSA_EXPORT MidiPort : public QObject { Q_OBJECT friend class MidiClient; public: explicit MidiPort( QObject* parent = nullptr ); virtual ~MidiPort(); void attach( MidiClient* seq ); void detach(); void subscribe( Subscription* subs ); void unsubscribe( Subscription* subs ); void unsubscribeAll(); void unsubscribeTo( QString const& name ); void unsubscribeTo( PortInfo* port ); void unsubscribeTo( const snd_seq_addr_t* addr ); void unsubscribeFrom( QString const& name ); void unsubscribeFrom( PortInfo* port ); void unsubscribeFrom( const snd_seq_addr_t* addr ); void subscribeTo( PortInfo* port); void subscribeTo( int client, int port ); void subscribeTo( QString const& name ); void subscribeFrom( PortInfo* port ); void subscribeFrom( int client, int port ); void subscribeFrom( QString const& name ); void subscribeFromAnnounce(); void updateSubscribers(); SubscriptionsList getSubscriptions() const; PortInfoList getReadSubscribers(); PortInfoList getWriteSubscribers(); void updateConnectionsTo(const PortInfoList& desired); void updateConnectionsFrom(const PortInfoList& desired); static bool containsAddress(const snd_seq_addr_t* addr, const PortInfoList& lst); void applyPortInfo(); QString getPortName(); void setPortName( QString const& newName); int getPortId(); unsigned int getCapability(); void setCapability( unsigned int newValue); unsigned int getPortType(); void setPortType( unsigned int newValue); int getMidiChannels(); void setMidiChannels(int newValue); int getMidiVoices(); void setMidiVoices(int newValue); int getSynthVoices(); void setSynthVoices(int newValue); bool getTimestamping(); bool getTimestampReal(); int getTimestampQueue(); void setTimestamping(bool value); void setTimestampReal(bool value); void setTimestampQueue(int queueId); Q_SIGNALS: /** * Signal emitted when an internal subscription is done. * @param port MIDI port object pointer * @param subs Subscription object pointer */ void subscribed(drumstick::ALSA::MidiPort* port, drumstick::ALSA::Subscription* subs); /** * Signal emitted when the MidiClient has changed * @param port MIDI port object pinter * @param seq MidiClient object pointer */ void midiClientChanged(drumstick::ALSA::MidiPort* port, drumstick::ALSA::MidiClient* seq); /** * Signal emitted when the port is attached to a MidiClient * @param port MIDI port object pointer */ void attached(drumstick::ALSA::MidiPort* port); /** * Signal emitted when the port is detached from a MidiClient * @param port MIDI port object pointer */ void detached(drumstick::ALSA::MidiPort* port); protected: PortInfo* getPortInfo(); void freeSubscriptions(); void setMidiClient( MidiClient* seq ); private: MidiClient* m_MidiClient; PortInfo m_Info; bool m_Attached; SubscriptionsList m_Subscriptions; }; /** * List of Ports instances. */ typedef QList MidiPortList; /** @} */ }} /* namespace drumstick::ALSA */ #endif //DRUMSTICK_ALSAPORT_H drumstick-2.9.0/library/include/drumstick/alsatimer.h0000644000175000017500000002253614541630232021771 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DRUMSTICK_ALSATIMER_H #define DRUMSTICK_ALSATIMER_H extern "C" { #include } #include #include #include #include #include #include "macros.h" namespace drumstick { namespace ALSA { /** * @file alsatimer.h * Classes managing ALSA Timers */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_ALSA_EXPORT #else #if defined(drumstick_alsa_EXPORTS) #define DRUMSTICK_ALSA_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_ALSA_EXPORT Q_DECL_IMPORT #endif #endif class TimerQuery; class TimerId; class TimerGlobalInfo; /** * @addtogroup ALSATimer ALSA Timers * @{ * * @class TimerInfo * ALSA Timer information container. * * This class is used to hold properties about ALSA Timers. */ class DRUMSTICK_ALSA_EXPORT TimerInfo { friend class Timer; public: TimerInfo(); TimerInfo(const TimerInfo& other); explicit TimerInfo(const snd_timer_info_t* other); virtual ~TimerInfo(); TimerInfo* clone(); TimerInfo& operator=(const TimerInfo& other); int getSizeOfInfo() const; bool isSlave(); int getCard(); QString getId(); QString getName(); long getResolution(); long getFrequency(); protected: Q_DECL_DEPRECATED long getTicks(); private: snd_timer_info_t *m_Info; }; /** * ALSA Timer identifier container. * * This class provides an unique identifier for a Timer. */ class DRUMSTICK_ALSA_EXPORT TimerId { friend class TimerQuery; friend class TimerGlobalInfo; friend class QueueTimer; public: TimerId(); TimerId(const TimerId& other); explicit TimerId(const snd_timer_id_t *other); TimerId(int cls, int scls, int card, int dev, int sdev); virtual ~TimerId(); TimerId* clone(); TimerId& operator=(const TimerId& other); int getSizeOfInfo() const; void setClass(int devclass); int getClass(); void setSlaveClass(int devsclass); int getSlaveClass(); void setCard(int card); int getCard(); void setDevice(int device); int getDevice(); void setSubdevice(int subdevice); int getSubdevice(); private: snd_timer_id_t *m_Info; }; /** * List of timer identifiers */ typedef QList TimerIdList; /** * Global timer information container. * * This class provides global timer parameters. */ class DRUMSTICK_ALSA_EXPORT TimerGlobalInfo { friend class TimerQuery; public: TimerGlobalInfo(); TimerGlobalInfo(const TimerGlobalInfo& other); explicit TimerGlobalInfo(const snd_timer_ginfo_t* other); virtual ~TimerGlobalInfo(); TimerGlobalInfo* clone(); TimerGlobalInfo& operator=(const TimerGlobalInfo& other); int getSizeOfInfo() const; void setTimerId(const TimerId& tid); TimerId& getTimerId(); unsigned int getFlags(); int getCard(); QString getId(); QString getName(); unsigned long getResolution(); unsigned long getMinResolution(); unsigned long getMaxResolution(); unsigned int getClients(); private: snd_timer_ginfo_t* m_Info; TimerId m_Id; }; /** * ALSA Timer inquiry helper. * * This class provides a mechanism to enumerate the available system timers. */ class DRUMSTICK_ALSA_EXPORT TimerQuery { public: TimerQuery(const QString& deviceName, int openMode); TimerQuery(const QString& deviceName, int openMode, snd_config_t* conf); virtual ~TimerQuery(); /** * Gets the list of available timers * @return List of TimerId objects */ TimerIdList getTimers() const { return m_timers; } TimerGlobalInfo& getGlobalInfo(); void setGlobalParams(snd_timer_gparams_t* params); void getGlobalParams(snd_timer_gparams_t* params); void getGlobalStatus(snd_timer_gstatus_t* status); protected: void readTimers(); void freeTimers(); private: snd_timer_query_t *m_Info; TimerIdList m_timers; TimerGlobalInfo m_GlobalInfo; }; /** * ALSA Timer parameters container. * * This class provides several parameters about a Timer. */ class DRUMSTICK_ALSA_EXPORT TimerParams { friend class Timer; public: TimerParams(); TimerParams(const TimerParams& other); explicit TimerParams(const snd_timer_params_t* other); virtual ~TimerParams(); TimerParams* clone(); TimerParams& operator=(const TimerParams& other); int getSizeOfInfo() const; void setAutoStart(bool auto_start); bool getAutoStart(); void setExclusive(bool exclusive); bool getExclusive(); void setEarlyEvent(bool early_event); bool getEarlyEvent(); void setTicks(long ticks); long getTicks(); void setQueueSize(long queue_size); long getQueueSize(); void setFilter(unsigned int filter); unsigned int getFilter(); private: snd_timer_params_t* m_Info; }; /** * ALSA Timer status container. * * This class provides some status information about a Timer. */ class DRUMSTICK_ALSA_EXPORT TimerStatus { friend class Timer; public: TimerStatus(); TimerStatus(const TimerStatus& other); explicit TimerStatus(const snd_timer_status_t* other); virtual ~TimerStatus(); TimerStatus* clone(); TimerStatus& operator=(const TimerStatus& other); int getSizeOfInfo() const; snd_htimestamp_t getTimestamp(); long getResolution(); long getLost(); long getOverrun(); long getQueue(); private: snd_timer_status_t* m_Info; }; /** * ALSA Timer events handler. * * This abstract class is used to define an interface that other class can * implement to receive timer events. */ class DRUMSTICK_ALSA_EXPORT TimerEventHandler { public: /** Destructor */ virtual ~TimerEventHandler() = default; /** * Timer event handler. This method is called when the timer expires. * @param ticks The time in ticks. * @param msecs The time in milliseconds. */ virtual void handleTimerEvent(int ticks, int msecs) = 0; }; /** * ALSA Timer management. * * This class represents an ALSA timer object. */ class DRUMSTICK_ALSA_EXPORT Timer : public QObject { Q_OBJECT private: /** * This class manages timer events input from ALSA */ class TimerInputThread : public QThread { public: /** Constructor */ TimerInputThread(Timer* t, int timeout) : QThread(), m_timer(t), m_Wait(timeout), m_Stopped(false) {} /** Destructor */ virtual ~TimerInputThread() = default; void run() override; bool stopped(); void stop(); private: Timer* m_timer; int m_Wait; bool m_Stopped; QReadWriteLock m_mutex; }; public: Timer(int cls, int scls, int card, int dev, int sdev, int openMode, QObject* parent = nullptr); Timer(const QString& deviceName, int openMode, QObject* parent = nullptr); Timer(const QString& deviceName, int openMode, snd_config_t* config, QObject* parent = nullptr); Timer(TimerId& id, int openMode, QObject* parent = nullptr); virtual ~Timer(); static TimerId bestGlobalTimerId(); static Timer* bestGlobalTimer(int openMode, QObject* parent = nullptr); /** * Gets the ALSA timer object. * @return ALSA timer object pointer. */ snd_timer_t* getHandle() { return m_Info; } TimerInfo& getTimerInfo(); TimerStatus& getTimerStatus(); void setTimerParams(const TimerParams& params); void start(); void stop(); void continueRunning(); void addAsyncTimerHandler(snd_async_callback_t callback, void *private_data); int getPollDescriptorsCount(); void pollDescriptors(struct pollfd *pfds, unsigned int space); void pollDescriptorsRevents(struct pollfd *pfds, unsigned int nfds, unsigned short *revents); ssize_t read(void *buffer, size_t size); snd_timer_t* getTimerHandle(); /** * Sets an event handler providing a method to be called when a timer expires. * @param h A TimerEventHandler instance. */ void setHandler(TimerEventHandler* h) { m_handler = h; } void startEvents(); void stopEvents(); protected: void doEvents(); Q_SIGNALS: /** * This signal is emitted when the timer has expired, if there is not an * event hander installed. * * @param ticks The time in ticks. * @param msecs The time in milliseconds. */ void timerExpired(int ticks, int msecs); private: snd_timer_t *m_Info; snd_async_handler_t *m_asyncHandler; TimerEventHandler* m_handler; QPointer m_thread; TimerInfo m_TimerInfo; TimerStatus m_TimerStatus; QString m_deviceName; snd_htimestamp_t m_last_time; }; /** @} */ }} /* namespace drumstick::ALSA */ #endif /* DRUMSTICK_ALSATIMER_H */ drumstick-2.9.0/library/include/drumstick/pianokeybd.h0000644000175000017500000002771514541630232022141 0ustar pedropedro/* Virtual Piano Widget for Qt Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #ifndef PIANOKEYBD_H #define PIANOKEYBD_H #include #include #include #include "macros.h" #include "pianopalette.h" /** * @file pianokeybd.h * Piano Keyboard Widget */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_WIDGETS_EXPORT #else #if defined(drumstick_widgets_EXPORTS) #define DRUMSTICK_WIDGETS_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_WIDGETS_EXPORT Q_DECL_IMPORT #endif #endif namespace drumstick { namespace widgets { Q_NAMESPACE /** * @addtogroup Widgets * @{ * * @class RawKbdHandler * @brief The RawKbdHandler class callbacks * * RawKbdHandler provides callbacks for low level computer keyboard events */ class RawKbdHandler { public: virtual ~RawKbdHandler() = default; /** * @brief handleKeyPressed handles low level computer keyboard press events * @param keycode The low level key code pressed * @return whether the event has been processed or not */ virtual bool handleKeyPressed(int keycode) = 0; /** * @brief handleKeyReleased handles low level computer keyboard reelase events * @param keycode The low level key code released * @return whether the event has been processed or not */ virtual bool handleKeyReleased(int keycode) = 0; }; /** * @brief The PianoHandler class callbacks * * This class provides handler methods for note events. This class must be * inherited and implemented by a program using also PianoKeybd to receive the * note events generated from a computer keyboard, mouse or touch events. It is * provided using the method @ref PianoKeybd::setPianoHandler() and can be retrieved * using @ref PianoKeybd::getPianoHandler(). This mechanism is an option alternative * to proces signals @ref PianoKeybd::noteOn() and @ref PianoKeybd::noteOff() . */ class PianoHandler { public: virtual ~PianoHandler() = default; /** * @brief noteOn handles MIDI note on events * @param note MIDI note number * @param vel MIDI velocity */ virtual void noteOn( const int note, const int vel ) = 0; /** * @brief noteOff handles MIDI note off events * @param note MIDI note number * @param vel MIDI velocity */ virtual void noteOff( const int note, const int vel ) = 0; }; /** * @brief KeyboardMap * * KeyboardMap represents a mapping definition to translate from * computer keyboard keys and MIDI notes, either for alphanumeric * or low level (raw) events. */ typedef QHash KeyboardMap; extern DRUMSTICK_WIDGETS_EXPORT KeyboardMap g_DefaultKeyMap; ///< Global Key Map Variable extern DRUMSTICK_WIDGETS_EXPORT KeyboardMap g_DefaultRawKeyMap; ///< Global Raw Key Map Variable const int DEFAULTSTARTINGKEY = 9; ///< Default starting key (A) const int DEFAULTBASEOCTAVE = 1; ///< Default base octave const int DEFAULTNUMBEROFKEYS = 88; ///< Default number of piano keys /** * @brief Labels Visibility */ enum LabelVisibility { ShowNever, ///< Don't show note names ShowMinimum, ///< Show only note C names ShowActivated, ///< Show names when notes are activated ShowAlways ///< Show always note names }; Q_ENUM_NS(LabelVisibility) /** * @brief Labels for Alterations */ enum LabelAlteration { ShowSharps, ///< Show sharps on black keys ShowFlats, ///< Show flats on black keys ShowNothing ///< Do not show names on black keys }; Q_ENUM_NS(LabelAlteration) /** * @brief Labels Orientation */ enum LabelOrientation { HorizontalOrientation, ///< Show horizontal names VerticalOrientation, ///< Show vertical names AutomaticOrientation ///< Show horizonal or vertical names depending on the size }; Q_ENUM_NS(LabelOrientation) /** * @brief Labels Naming */ enum LabelNaming { StandardNames, ///< Show standard names CustomNamesWithSharps, ///< Show custom names with sharps CustomNamesWithFlats ///< Show custom names with flats }; Q_ENUM_NS(LabelNaming) /** * @brief Labels Central Octave */ enum LabelCentralOctave { OctaveNothing = -1, ///< Don't show octave numbers OctaveC3, ///< Central C, MIDI note #60 is C3 OctaveC4, ///< Central C, MIDI note #60 is C4 OctaveC5 ///< Central C, MIDI note #60 is C5 }; Q_ENUM_NS(LabelCentralOctave) /** * @brief The PianoKeybd class * * This class is a widget providing the look and behavior of a musical piano keyboard. * It is implemented as a QGraphicsView displaying the contents of a QGraphicsScene (PianoScene). */ class DRUMSTICK_WIDGETS_EXPORT PianoKeybd : public QGraphicsView, public RawKbdHandler { Q_OBJECT Q_PROPERTY( int baseOctave READ baseOctave WRITE setBaseOctave ) Q_PROPERTY( int numKeys READ numKeys WRITE setNumKeys ) Q_PROPERTY( int rotation READ getRotation WRITE setRotation ) Q_PROPERTY( QColor keyPressedColor READ getKeyPressedColor WRITE setKeyPressedColor ) Q_PROPERTY( drumstick::widgets::LabelVisibility showLabels READ showLabels WRITE setShowLabels ) Q_PROPERTY( drumstick::widgets::LabelAlteration alterations READ labelAlterations WRITE setLabelAlterations ) Q_PROPERTY( drumstick::widgets::LabelOrientation labelOrientation READ labelOrientation WRITE setLabelOrientation ) Q_PROPERTY( drumstick::widgets::LabelCentralOctave labelOctave READ labelOctave WRITE setLabelOctave ) Q_PROPERTY( int transpose READ getTranspose WRITE setTranspose ) Q_PROPERTY( int startKey READ startKey WRITE setStartKey ) Q_PROPERTY( QFont labelFont READ font WRITE setFont ) #ifndef Q_MOC_RUN Q_CLASSINFO("Author", "Pedro Lopez-Cabanillas ") Q_CLASSINFO("URL", "https://sourceforge.net/projects/drumstick") Q_CLASSINFO("Version", QT_STRINGIFY(VERSION)) #endif public: explicit PianoKeybd(QWidget *parent = nullptr); PianoKeybd(const int baseOctave, const int numKeys, const int startKey, QWidget *parent = nullptr); virtual ~PianoKeybd(); void setFont(const QFont &font); PianoHandler* getPianoHandler() const; void setPianoHandler(PianoHandler* handler); PianoPalette getHighlightPalette() const; void setHighlightPalette(const PianoPalette& p ); PianoPalette getBackgroundPalette() const; void setBackgroundPalette(const PianoPalette& p ); PianoPalette getForegroundPalette() const; void setForegroundPalette(const PianoPalette& p ); bool showColorScale() const; void setShowColorScale(const bool show); void useCustomNoteNames(const QStringList& names); void useStandardNoteNames(); QStringList customNoteNames() const; QStringList standardNoteNames() const; void retranslate(); int baseOctave() const; void setBaseOctave(const int baseOctave); int numKeys() const; int startKey() const; void setNumKeys(const int numKeys, const int startKey = DEFAULTSTARTINGKEY); int getRotation() const; void setRotation(int r); QColor getKeyPressedColor() const; void setKeyPressedColor(const QColor& c); void resetKeyPressedColor(); LabelVisibility showLabels() const; void setShowLabels(const LabelVisibility show); LabelAlteration labelAlterations() const; void setLabelAlterations(const LabelAlteration use); LabelOrientation labelOrientation() const; void setLabelOrientation(const LabelOrientation orientation); LabelCentralOctave labelOctave() const; void setLabelOctave(const LabelCentralOctave octave); int getTranspose() const; void setTranspose(int t); int getChannel() const; void setChannel(const int c); int getVelocity() const; void setVelocity(const int v); bool isKeyboardEnabled() const; void setKeyboardEnabled( const bool enable ); bool isMouseEnabled() const; void setMouseEnabled( const bool enable ); bool isTouchEnabled() const; void setTouchEnabled( const bool enable ); bool velocityTint() const ; void setVelocityTint( const bool enable ); void allKeysOff(); QSize sizeHint() const override; void setKeyboardMap(KeyboardMap* m); KeyboardMap* getKeyboardMap(); void resetKeyboardMap(); void setRawKeyboardMap(KeyboardMap* m); KeyboardMap* getRawKeyboardMap(); void resetRawKeyboardMap(); bool getRawKeyboardMode() const; void setRawKeyboardMode(const bool b); void showNoteOn( const int note, QColor color, int vel = -1 ); void showNoteOn( const int note, int vel = -1 ); void showNoteOff( const int note, int vel = -1 ); // RawKbdHandler methods bool handleKeyPressed(int keycode) override; bool handleKeyReleased(int keycode) override; void setKeyPicture(const bool natural, const QPixmap& pix); QPixmap getKeyPicture(const bool natural); void setUseKeyPictures(const bool enable); bool getUseKeyPictures() const; void setUsingNativeFilter(const bool newState); bool isUsingNativeFilter() const; void setOctaveSubscript(const bool enable); bool octaveSubscript() const; void setStartKey(const int startKey); Q_SIGNALS: /** * This signal is emitted for each Note On MIDI event created using * the computer keyboard, mouse or touch screen. It is not emitted if * a PianoHandler has been assigned using setPianoHandler(). * @param midiNote the MIDI note number * @param vel the MIDI velocity */ void noteOn( int midiNote, int vel ); /** * This signal is emitted for each Note Off MIDI event created using * the computer keyboard, mouse or touch screen. It is not emitted if * a PianoHandler has been assigned using setPianoHandler(). * @param midiNote the MIDI note number * @param vel the MIDI velocity */ void noteOff( int midiNote, int vel ); /** * signalName is emitted for each note created, and contains a string * with the MIDI note number and the note name for each note on event. * @param name the MIDI note number and name */ void signalName( const QString& name ); protected: void initialize(); void initDefaultMap(); void initScene(int base, int num, int ini, const QColor& c = QColor()); void resizeEvent(QResizeEvent *event) override; bool viewportEvent(QEvent *ev) override; private: class PianoKeybdPrivate; QScopedPointer d; }; /** @} */ }} // namespace drumstick::widgets #endif // PIANOKEYBD_H drumstick-2.9.0/library/include/drumstick/playthread.h0000644000175000017500000000761314541630232022144 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DRUMSTICK_PLAYTHREAD_H #define DRUMSTICK_PLAYTHREAD_H #include "alsaevent.h" #include #include namespace drumstick { namespace ALSA { /** * @file playthread.h * Sequencer output thread */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_ALSA_EXPORT #else #if defined(drumstick_alsa_EXPORTS) #define DRUMSTICK_ALSA_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_ALSA_EXPORT Q_DECL_IMPORT #endif #endif class MidiClient; class MidiQueue; /** * @addtogroup PlayThread ALSA Sequencer Output * @{ * * @class SequencerOutputThread * Sequence player auxiliary class * * This class is used to implement an asynchronous sequence player using * ALSA sequencer scheduling * * Examples: guiplayer.cpp and playsmf.cpp */ class DRUMSTICK_ALSA_EXPORT SequencerOutputThread : public QThread { Q_OBJECT public: SequencerOutputThread(MidiClient *seq, int portId); virtual void run() override; /** * Gets the initial position in ticks of the sequence. The * default value zero means starting from the beginning. * @return Initial position (ticks) */ virtual unsigned int getInitialPosition() { return 0; } /** * Gets the echo event resolution in ticks. This is the time * between echo events interleaved with the MIDI sequence. The default * value zero means that no echo events are sent at all. * @return Echo resolution (ticks) */ virtual unsigned int getEchoResolution() { return 0; } /** * Check if there is one more event in the sequence. * This is a pure virtual method that must be overridden in the derived * class. * @return True if the sequence has another event. */ virtual bool hasNext() = 0; /** * Gets the next event in the sequence. * This is a pure virtual function that must be overridden in the derived * class. * @return Pointer to the next SequencerEvent to be played. */ virtual SequencerEvent* nextEvent() = 0; /** * Stops playing the current sequence. */ virtual void stop(); Q_SIGNALS: /** * Signal emitted when the sequence play-back has finished. */ void playbackFinished(); /** * Signal emitted when the play-back has stopped. */ void playbackStopped(); public Q_SLOTS: void start( QThread::Priority priority = InheritPriority ); protected: virtual void sendEchoEvent(int tick); virtual void sendSongEvent(SequencerEvent* ev); virtual void drainOutput(); virtual void syncOutput(); virtual bool stopRequested(); MidiClient *m_MidiClient; /**< MidiClient instance pointer */ MidiQueue *m_Queue; /**< MidiQueue instance pointer */ int m_PortId; /**< MidiPort numeric identifier */ bool m_Stopped; /**< Stopped status */ int m_QueueId; /**< MidiQueue numeric identifier */ int m_npfds; /**< Number of pollfd pointers */ pollfd* m_pfds; /**< Array of pollfd pointers */ QReadWriteLock m_mutex; /**< Mutex object used for synchronization */ }; /** @} */ }} /* namespace drumstick::ALSA */ #endif /*DRUMSTICK_PLAYTHREAD_H*/ drumstick-2.9.0/library/include/drumstick/qsmf.h0000644000175000017500000002776614541630232020770 0ustar pedropedro/* Standard MIDI File component Copyright (C) 2006-2023, Pedro Lopez-Cabanillas Based on midifile.c by Tim Thompson, M.Czeiszperger and Greg Lee This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DRUMSTICK_QSMF_H #define DRUMSTICK_QSMF_H #include "macros.h" #include #include class QTextCodec; class QDataStream; /** * @file qsmf.h * Standard MIDI Files Input/Output */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_FILE_EXPORT #else #if defined(drumstick_file_EXPORTS) #define DRUMSTICK_FILE_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_FILE_EXPORT Q_DECL_IMPORT #endif #endif namespace drumstick { /** * @ingroup File * @brief Drumstick File library */ namespace File { /** * @addtogroup SMF Standard MIDI Files Management (I/O) * @{ */ const quint32 MThd = 0x4d546864; /**< SMF Header prefix */ const quint32 MTrk = 0x4d54726b; /**< SMF Track prefix */ /* Standard MIDI Files meta event definitions */ const quint8 meta_event = 0xff; /**< SMF Meta Event prefix */ const quint8 sequence_number = 0x00; /**< SMF Sequence number */ const quint8 text_event = 0x01; /**< SMF Text event */ const quint8 copyright_notice = 0x02; /**< SMF Copyright notice */ const quint8 sequence_name = 0x03; /**< SMF Sequence name */ const quint8 instrument_name = 0x04; /**< SMF Instrument name */ const quint8 lyric = 0x05; /**< SMF Lyric */ const quint8 marker = 0x06; /**< SMF Marker */ const quint8 cue_point = 0x07; /**< SMF Cue point */ const quint8 forced_channel = 0x20; /**< SMF Forced MIDI channel */ const quint8 forced_port = 0x21; /**< SMF Forced MIDI port */ const quint8 end_of_track = 0x2f; /**< SMF End of track */ const quint8 set_tempo = 0x51; /**< SMF Tempo change */ const quint8 smpte_offset = 0x54; /**< SMF SMPTE offset */ const quint8 time_signature = 0x58; /**< SMF Time signature */ const quint8 key_signature = 0x59; /**< SMF Key signature */ const quint8 sequencer_specific = 0x7f; /**< SMF Sequencer specific */ /* MIDI status commands most significant bit is 1 */ const quint8 note_off = 0x80; /**< MIDI event Note Off */ const quint8 note_on = 0x90; /**< MIDI event Note On */ const quint8 poly_aftertouch = 0xa0; /**< MIDI event Polyphonic pressure */ const quint8 control_change = 0xb0; /**< MIDI event Control change */ const quint8 program_chng = 0xc0; /**< MIDI event Program change */ const quint8 channel_aftertouch = 0xd0; /**< MIDI event Channel after-touch */ const quint8 pitch_wheel = 0xe0; /**< MIDI event Bender */ const quint8 system_exclusive = 0xf0; /**< MIDI event System Exclusive begin */ const quint8 end_of_sysex = 0xf7; /**< MIDI event System Exclusive end */ const quint8 midi_command_mask = 0xf0; /**< Mask to extract the command from the status byte */ const quint8 midi_channel_mask = 0x0f; /**< Mask to extract the channel from the status byte */ const quint8 major_mode = 0; /**< Major mode scale */ const quint8 minor_mode = 1; /**< Minor mode scale */ /** * Standard MIDI Files input/output * * This class is used to parse and encode Standard MIDI Files (SMF) */ class DRUMSTICK_FILE_EXPORT QSmf : public QObject { Q_OBJECT public: explicit QSmf(QObject * parent = nullptr); virtual ~QSmf(); void readFromStream(QDataStream *stream); void readFromFile(const QString& fileName); void writeToStream(QDataStream *stream); void writeToFile(const QString& fileName); void writeMetaEvent(long deltaTime, int type, const QByteArray& data); void writeMetaEvent(long deltaTime, int type, const QString& data); void writeMetaEvent(long deltaTime, int type, int data); void writeMetaEvent(long deltaTime, int type); void writeMidiEvent(long deltaTime, int type, int chan, int b1); void writeMidiEvent(long deltaTime, int type, int chan, int b1, int b2); void writeMidiEvent(long deltaTime, int type, int chan, const QByteArray& data); void writeMidiEvent(long deltaTime, int type, long len, char* data); void writeTempo(long deltaTime, long tempo); void writeBpmTempo(long deltaTime, int tempo); void writeTimeSignature(long deltaTime, int num, int den, int cc, int bb); void writeKeySignature(long deltaTime, int tone, int mode); void writeSequenceNumber(long deltaTime, int seqnum); long getCurrentTime(); long getCurrentTempo(); long getRealTime(); long getFilePos(); int getDivision(); void setDivision(int division); int getTracks(); void setTracks(int tracks); int getFileFormat(); void setFileFormat(int fileFormat); Q_DECL_DEPRECATED QTextCodec* getTextCodec(); Q_DECL_DEPRECATED void setTextCodec(QTextCodec *codec); Q_SIGNALS: /** * Emitted for a SMF read or write error * @param errorStr Error string */ void signalSMFError(const QString& errorStr); /** * Emitted after reading a SMF header * @param format SMF format (0/1) * @param ntrks Number pof tracks * @param division Division (resolution in ticks per quarter note) */ void signalSMFHeader(int format, int ntrks, int division); /** * Emitted after reading a Note On message * @param chan MIDI Channel * @param pitch MIDI Note * @param vol Velocity */ void signalSMFNoteOn(int chan, int pitch, int vol); /** * Emitted after reading a Note Off message * @param chan MIDI Channel * @param pitch MIDI Note * @param vol Velocity */ void signalSMFNoteOff(int chan, int pitch, int vol); /** * Emitted after reading a Polyphonic Aftertouch message * @param chan MIDI Channel * @param pitch MIDI Note * @param press Pressure amount */ void signalSMFKeyPress(int chan, int pitch, int press); /** * Emitted after reading a Control Change message * @param chan MIDI Channel * @param ctl MIDI Controller * @param value Control value */ void signalSMFCtlChange(int chan, int ctl, int value); /** * Emitted after reading a Bender message * @param chan MIDI Channel * @param value Bender value */ void signalSMFPitchBend(int chan, int value); /** * Emitted after reading a Program change message * @param chan MIDI Channel * @param patch Program number */ void signalSMFProgram(int chan, int patch); /** * Emitted after reading a Channel Aftertouch message * @param chan MIDI Channel * @param press Pressure amount */ void signalSMFChanPress(int chan, int press); /** * Emitted after reading a System Exclusive message * @param data Sysex bytes */ void signalSMFSysex(const QByteArray& data); /** * Emitted after reading a Sequencer specific message * @param data Message data */ void signalSMFSeqSpecific(const QByteArray& data); /** * Emitted after reading an unregistered SMF Meta message * @param typ Message type * @param data Message data * @since 0.2.0 */ void signalSMFMetaUnregistered(int typ, const QByteArray& data); /** * Emitted after reading any SMF Meta message * @param typ Message type * @param data Message data */ void signalSMFMetaMisc(int typ, const QByteArray& data); /** * Emitted after reading a Sequence number message * @param seq Sequence number */ void signalSMFSequenceNum(int seq); /** * Emitted after reading a Forced channel message * @param channel MIDI Channel */ void signalSMFforcedChannel(int channel); /** * Emitted after reading a Forced port message * @param port Port number */ void signalSMFforcedPort(int port); /** * Emitted after reading a SMF text message * @param typ Text type * @param data Text data * @deprecated because the class QTextCodec was removed from QtCore since Qt6. * use signalSMFText2() instead. */ Q_DECL_DEPRECATED void signalSMFText(int typ, const QString& data); /** * Emitted after reading a SMF text message * @param typ Text type * @param data Text data */ void signalSMFText2(int typ, const QByteArray& data); /** * Emitted after reading a SMPT offset message * @param b0 Hours * @param b1 Minutes * @param b2 Seconds * @param b3 Frames * @param b4 Fractional frames */ void signalSMFSmpte(int b0, int b1, int b2, int b3, int b4); /** * Emitted after reading a SMF Time signature message * @param b0 Numerator * @param b1 Denominator (exponent in a power of two) * @param b2 Number of MIDI clocks per metronome click * @param b3 Number of notated 32nd notes per 24 MIDI clocks */ void signalSMFTimeSig(int b0, int b1, int b2, int b3); /** * Emitted after reading a SMF Key Signature smessage * @param b0 Number of alterations (negative=flats, positive=sharps) * @param b1 Scale Mode (0=major, 1=minor) */ void signalSMFKeySig(int b0, int b1); /** * Emitted after reading a Tempo Change message * @param tempo Microseconds per quarter note */ void signalSMFTempo(int tempo); /** * Emitted after reading a End-Of-Track message */ void signalSMFendOfTrack(); /** * Emitted after reading a track prefix */ void signalSMFTrackStart(); /** * Emitted after a track has finished */ void signalSMFTrackEnd(); /** * Emitted to request the user to prepare the tempo track. * * The signal handler should not call the writeTempo() or writeBpmTempo() * methods directly, but instead it should fill the Conductor track with * tempo and other SMF meta events like key and time signatures to be written * later, at the signalSMFWriteTrack() handler. * * The Conductor track is the first track in a format 1 SMF. */ void signalSMFWriteTempoTrack(); /** * Emitted to request the user to write a track. * @param track Track number */ void signalSMFWriteTrack(int track); private: /** * Tempo change within a SMF or sequence */ struct QSmfRecTempo { quint64 tempo; quint64 time; }; class QSmfPrivate; QScopedPointer d; void SMFRead(); void SMFWrite(); quint8 getByte(); void putByte(quint8 value); void readHeader(); void readTrack(); quint16 to16bit(quint8 c1, quint8 c2); quint32 to32bit(quint8 c1, quint8 c2, quint8 c3, quint8 c4); quint16 read16bit(); quint32 read32bit(); void write16bit(quint16 data); void write32bit(quint32 data); void writeVarLen(quint64 value); double ticksToSecs(quint64 ticks, quint16 division, quint64 tempo); long readVarLen(); void readExpected(const QString& s); void addTempo(quint64 tempo, quint64 time); quint64 findTempo(); void SMFError(const QString& s); void channelMessage(quint8 status, quint8 c1, quint8 c2); void msgInit(); void msgAdd(quint8 b); void metaEvent(quint8 b); void sysEx(); void badByte(quint8 b, int p); quint8 lowerByte(quint16 x); quint8 upperByte(quint16 x); bool endOfSmf(); void writeHeaderChunk(int format, int ntracks, int division); void writeTrackChunk(int track); }; QString DRUMSTICK_FILE_EXPORT drumstickLibraryVersion(); /** @} */ }} /* namespace drumstick::File */ #endif /* DRUMSTICK_QSMF_H */ drumstick-2.9.0/library/include/drumstick/rmid.h0000644000175000017500000001266114541630232020741 0ustar pedropedro/* Standard RIFF MIDI Component Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef RMIDI_H #define RMIDI_H #include #include #include #include #include "macros.h" /** * @file rmid.h * RIFF MIDI Files Input */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_FILE_EXPORT #else #if defined(drumstick_file_EXPORTS) #define DRUMSTICK_FILE_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_FILE_EXPORT Q_DECL_IMPORT #endif #endif namespace drumstick { namespace File { /** * @addtogroup RMID RIFF MIDI File Parser (Input) * @{ */ /** * RIFF MIDI file format (input only) * * This class is used to parse RIFF MIDI Files * @since 2.4.0 */ class DRUMSTICK_FILE_EXPORT Rmidi : public QObject { Q_OBJECT public: explicit Rmidi(QObject * parent = nullptr); virtual ~Rmidi(); void readFromFile(QString fileName); void readFromStream(QDataStream* ds); Q_SIGNALS: /** * @brief signalRMidInfo is emitted for each RIFF INFO element * @param infoType Type of data (chunk ID) as defined in the spec (Source: www.midi.org rp29spec.pdf). * * * IARL Archival Location. Indicates where the subject of the file is archived. * * IART Artist. Lists the artist (author) of the original subject of the file. For example: Les Getalong. * * ICMS Commissioned. Lists the name of the person or organization that commissioned the subject of the file. For example: Acme Consolidated GameWorks. * * ICMT Comments. Provides general comments about the file or the subject of the file. If the comment is several sentences long, end each sentence with a period. Do not include newline characters. * * ICOP Copyright. Records the copyright information for the file. For example: Copyright Acme Consolidated GameWorks 1991. If there are multiple copyrights, separate them by a semicolon followed by a space. * * ICRD Creation date. Specifies the date the subject of the file was created. List dates in year-month-day format, padding one-digit months and days with a zero on the left. For example: 1553-05-03 for May 3, 1553. The year should always be given using four digits. * * IENG Engineer. Stores the name of the engineer who worked on the file. If there are multiple engineers, separate the names by a semicolon and a blank. For example: Smith, John; Adams, Joe. * * IGNR Genre. Describes the original work, such as jazz, classical, rock, techno, rave, neo british pop grunge metal, etc. * * IKEY Keywords. Provides a list of keywords that refer to the file or subject of the file. Separate multiple keywords with a semicolon and a blank. For example: FX; visitation; space alien. * * IMED Medium. Describes the original subject of the file, such as record, CD and so forth. * * INAM Name. Stores the title of the subject of the file, such as Seattle From Above. * * IPRD Product. Specifies the name of the title the file was originally intended for, such as Galactic Ambassadors V. * * ISBJ Subject. Describes the contents of the file, such as Music of the Gnu Whirled Order. * * ISFT Software. Identifies the name of the software package used to create the file, such as Crash Compactor, Acme Consolidated Sonic Booms. * * ISRC Source. Identifies the name of the person or organization who supplied the original subject of the file. For example: Acme Hysterical Media Archives. * * ISRF Source Form. Identifies the original form of the material that was digitized, such as record, sampling CD, TV sound track and so forth. This is not necessarily the same as IMED. * * ITCH Technician. Identifies the technician who sampled the subject file. For example: Smith, John. * * @param info Text data */ void signalRiffInfo(const QString& infoType, const QByteArray& info); /** * @brief signalRiffData is emitted for each RMID data element * * The handler of this event should use the method QSmf::readFromStream() to * parse the contents of the SMF data element. * * @param dataType may be "RMID" (SMF) or "DLS" * @param data binary payload, in RMID files is either a Standard MIDI File or a DLS structure */ void signalRiffData(const QString& dataType, const QByteArray& data); private: void read(); void processINFO(int size); void processList(int size); void processRMID(int size); void processRIFF(int size); void processData(const QString& dataType, int size); void skip(quint32 cktype, int size); quint32 readExpectedChunk(quint32 cktype); quint32 readChunk(quint32& cktype); quint32 readChunkID(); QByteArray readByteArray(int size); QString toString(quint32 ckid); private: QDataStream *m_stream; QString m_fileName; }; /** @} */ }} // namespace drumstick::File #endif // RMIDI_H drumstick-2.9.0/library/include/drumstick/settingsfactory.h0000644000175000017500000000367414541630232023242 0ustar pedropedro/* Drumstick Widgets Copyright (C) 2018-2023 Pedro Lopez-Cabanillas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SETTINGS_H #define SETTINGS_H #include #include #include "macros.h" /** * @file settingsfactory.h * SettingsFactory class declaration */ #if defined(DRUMSTICK_STATIC) #define DRUMSTICK_WIDGETS_EXPORT #else #if defined(drumstick_widgets_EXPORTS) #define DRUMSTICK_WIDGETS_EXPORT Q_DECL_EXPORT #else #define DRUMSTICK_WIDGETS_EXPORT Q_DECL_IMPORT #endif #endif /** * @class QSettings * @brief The QSettings class provides persistent platform-independent application settings. * @see https://doc.qt.io/qt-5/qsettings.html */ namespace drumstick { namespace widgets { /** * @addtogroup Widgets * @{ * * @class SettingsFactory * @brief The SettingsFactory class holds a global QSettings object. * This class creates and returns a QSettings object globally configured * instance using native or file storage. */ class DRUMSTICK_WIDGETS_EXPORT SettingsFactory { public: QSettings *getQSettings(); QSettings *operator->(); static void setFileName(const QString name); static QString fileName(); static QSettings::Format format(); private: QScopedPointer m_settings{nullptr}; static QString s_fileName; }; /** @} */ }} // namespace drumstick::widgets #endif // SETTINGS_H drumstick-2.9.0/library/library.pro0000644000175000017500000000041514541630232016365 0ustar pedropedroTEMPLATE = subdirs SUBDIRS += \ file \ rt \ rt-backends \ vpiano-plugin \ widgets rt-backends.depends += rt vpiano-plugin.depends += widgets linux { SUBDIRS += alsa rt-backends.depends += alsa } macx { OTHER_FILES += Info.plist.lib } drumstick-2.9.0/library/rt-backends/0000755000175000017500000000000014541630232016374 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/alsa-in/0000755000175000017500000000000014541630232017720 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/alsa-in/alsa-in.pro0000644000175000017500000000063514541630232021772 0ustar pedropedroTEMPLATE = lib CONFIG += c++11 plugin static { CONFIG += staticlib create_prl } TARGET = drumstick-rt-alsa-in DESTDIR = ../../../build/lib/drumstick2 DEPENDPATH += . ../../include INCLUDEPATH += . ../../include include (../../../global.pri) QT -= gui HEADERS += alsamidiinput.h SOURCES += alsamidiinput.cpp LIBS += -L../../../build/lib \ -ldrumstick-rt \ -ldrumstick-alsa \ -lasound drumstick-2.9.0/library/rt-backends/alsa-in/alsamidiinput.h0000644000175000017500000000445414541630232022743 0ustar pedropedro/* Drumstick RT Backend using the ALSA Sequencer Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ALSAMIDIInput_H #define ALSAMIDIInput_H #include #include #include namespace drumstick { namespace rt { class ALSAMIDIInput: public MIDIInput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIInput/2.0") Q_INTERFACES(drumstick::rt::MIDIInput) Q_PROPERTY(QStringList diagnostics READ getDiagnostics) Q_PROPERTY(bool status READ getStatus) public: explicit ALSAMIDIInput(QObject *parent = nullptr); virtual ~ALSAMIDIInput(); // MIDIInput interface virtual void initialize(QSettings* settings) override; virtual QString backendName() override; virtual QString publicName() override; virtual void setPublicName(QString name) override; virtual QList connections(bool advanced) override; virtual void setExcludedConnections(QStringList conns) override; virtual void open(const MIDIConnection& conn) override; virtual void close() override; virtual MIDIConnection currentConnection() override; virtual void setMIDIThruDevice(MIDIOutput *device) override; virtual void enableMIDIThru(bool enable) override; virtual bool isEnabledMIDIThru() override; static const QString DEFAULT_PUBLIC_NAME; private: class ALSAMIDIInputPrivate; ALSAMIDIInputPrivate * const d; private: QStringList getDiagnostics(); bool getStatus(); }; }} #endif /* ALSAMIDIInput_H */ drumstick-2.9.0/library/rt-backends/alsa-in/CMakeLists.txt0000644000175000017500000000456714541630232022474 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-rt-alsa-in_QTOBJ_SRCS alsamidiinput.h ) set(drumstick-rt-alsa-in_SRCS alsamidiinput.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt-alsa-in_MOC_SRCS ${drumstick-rt-alsa-in_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp(drumstick-rt-alsa-in_MOC_SRCS ${drumstick-rt-alsa-in_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library(drumstick-rt-alsa-in STATIC ${drumstick-rt-alsa-in_MOC_SRCS} ${drumstick-rt-alsa-in_SRCS}) target_compile_definitions(drumstick-rt-alsa-in PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-alsa-in PROPERTIES STATIC_LIB "libdrumstick-rt-alsa-in") else() add_library(drumstick-rt-alsa-in MODULE ${drumstick-rt-alsa-in_MOC_SRCS} ${drumstick-rt-alsa-in_SRCS}) target_compile_definitions(drumstick-rt-alsa-in PRIVATE QT_PLUGIN) endif() target_link_libraries(drumstick-rt-alsa-in PRIVATE Qt${QT_VERSION_MAJOR}::Core Drumstick::ALSA Drumstick::RT ) set_target_properties(drumstick-rt-alsa-in PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install(TARGETS drumstick-rt-alsa-in EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR}) drumstick-2.9.0/library/rt-backends/alsa-in/alsamidiinput.cpp0000644000175000017500000003334314541630232023275 0ustar pedropedro/* Drumstick RT Backend using the ALSA Sequencer Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "alsamidiinput.h" #include #include #include #include #include #include namespace drumstick { namespace rt { using namespace ALSA; const QString ALSAMIDIInput::DEFAULT_PUBLIC_NAME = QStringLiteral("MIDI In"); class ALSAMIDIInput::ALSAMIDIInputPrivate : public SequencerEventHandler { public: ALSAMIDIInput *m_inp; MIDIOutput *m_out; MidiClient *m_client; MidiPort *m_port; int m_portId; int m_clientId; bool m_thruEnabled; bool m_clientFilter; int m_runtimeAlsaNum; QString m_publicName; MIDIConnection m_currentInput; QList m_inputDevices; QStringList m_excludedNames; bool m_initialized; bool m_status; QStringList m_diagnostics; explicit ALSAMIDIInputPrivate(ALSAMIDIInput *inp) : m_inp(inp), m_out(nullptr), m_client(nullptr), m_port(nullptr), m_portId(-1), m_clientId(-1), m_thruEnabled(false), m_clientFilter(false), m_publicName(ALSAMIDIInput::DEFAULT_PUBLIC_NAME), m_initialized(false), m_status(false) { m_runtimeAlsaNum = getRuntimeALSALibraryNumber(); } virtual ~ALSAMIDIInputPrivate() { if (m_initialized) { clearSubscription(); uninitialize(); } } void initialize() { if (!m_initialized) { m_client = new MidiClient(m_inp); m_client->open(); m_client->setClientName(m_publicName); m_port = m_client->createPort(); m_port->setPortName("in"); m_port->setCapability( SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE ); m_port->setPortType( SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_MIDI_GENERIC ); m_clientId = m_client->getClientId(); m_portId = m_port->getPortId(); m_port->setTimestamping(false); m_port->setTimestampReal(false); m_client->setHandler(this); m_initialized = true; m_status = true; m_diagnostics.clear(); m_client->startSequencerInput(); reloadDeviceList(false); } //qDebug() << Q_FUNC_INFO << m_initialized; } void uninitialize() { if (m_initialized) { if (m_port != nullptr) { m_port->detach(); delete m_port; m_port = nullptr; } if (m_client != nullptr) { m_client->close(); delete m_client; m_client = nullptr; } m_initialized = false; m_status = false; m_diagnostics.clear(); } //qDebug() << Q_FUNC_INFO << m_initialized; } bool clientIsAdvanced(int clientId) { // asking for runtime version instead of SND_LIB_VERSION if (m_runtimeAlsaNum < 0x01000B) // ALSA <= 1.0.10 return (clientId < 64); else // ALSA >= 1.0.11 return (clientId < 16); } void reloadDeviceList(bool advanced) { QStringList clientNames; QMap namesMap; if (!m_initialized) { initialize(); } auto inputs = m_client->getAvailableInputs(); m_clientFilter = !advanced; m_inputDevices.clear(); m_inputDevices << MIDIConnection(); for (const PortInfo &p : std::as_const(inputs)) { QString name = p.getClientName(); clientNames << name; if (namesMap.contains(name)) { namesMap[name]++; } else { namesMap.insert(name, 1); } } for (PortInfo& p : inputs) { bool excluded = false; QString name = p.getClientName(); if (m_clientFilter && clientIsAdvanced(p.getClient())) continue; if ( m_clientFilter && name.startsWith(QStringLiteral("Virtual Raw MIDI")) ) continue; if ( name.startsWith(m_publicName) ) continue; for (const QString &n : std::as_const(m_excludedNames)) { if (name.startsWith(n)) { excluded = true; break; } } if (!excluded) { int k = clientNames.count(name) + 1; QString addr = QString("%1:%2").arg(p.getClient()).arg(p.getPort()); if (k > 2) { m_inputDevices << MIDIConnection(QString("%1 (%2)").arg(name).arg(k - namesMap[name]--), addr); } else { m_inputDevices << MIDIConnection(name, addr); } } } if (!m_currentInput.first.isEmpty() && !m_inputDevices.contains(m_currentInput)) { m_currentInput = MIDIConnection(); } } bool setSubscription(const MIDIConnection &newDevice) { //qDebug() << Q_FUNC_INFO << newDevice; if (!m_initialized) { initialize(); } if (m_inputDevices.contains(newDevice)) { m_currentInput = newDevice; m_port->unsubscribeAll(); m_port->subscribeFrom(newDevice.second.toString()); return true; } return false; } void clearSubscription() { //qDebug() << Q_FUNC_INFO; if (!m_currentInput.first.isEmpty() && m_initialized) { m_client->stopSequencerInput(); m_port->unsubscribeAll(); m_currentInput = MIDIConnection(); } } void setPublicName(QString newName) { if (newName != m_publicName) { m_publicName = newName; if(m_initialized) { m_client->setClientName(newName); } } } void handleSequencerEvent(SequencerEvent* ev) override { //qDebug() << Q_FUNC_INFO; if ( !SequencerEvent::isConnectionChange(ev) && m_initialized) switch(ev->getSequencerType()) { case SND_SEQ_EVENT_NOTEOFF: { const NoteOffEvent* n = static_cast(ev); if(m_out != nullptr && m_thruEnabled) { m_out->sendNoteOff(n->getChannel(), n->getKey(), n->getVelocity()); } Q_EMIT m_inp->midiNoteOff(n->getChannel(), n->getKey(), n->getVelocity()); } break; case SND_SEQ_EVENT_NOTEON: { const NoteOnEvent* n = static_cast(ev); if(m_out != nullptr && m_thruEnabled) { m_out->sendNoteOn(n->getChannel(), n->getKey(), n->getVelocity()); } Q_EMIT m_inp->midiNoteOn(n->getChannel(), n->getKey(), n->getVelocity()); } break; case SND_SEQ_EVENT_KEYPRESS: { const KeyPressEvent* n = static_cast(ev); if(m_out != nullptr && m_thruEnabled) { m_out->sendKeyPressure(n->getChannel(), n->getKey(), n->getVelocity()); } Q_EMIT m_inp->midiKeyPressure(n->getChannel(), n->getKey(), n->getVelocity()); } break; case SND_SEQ_EVENT_CONTROLLER: case SND_SEQ_EVENT_CONTROL14: { const ControllerEvent* n = static_cast(ev); if(m_out != nullptr && m_thruEnabled) { m_out->sendController(n->getChannel(), n->getParam(), n->getValue()); } Q_EMIT m_inp->midiController(n->getChannel(), n->getParam(), n->getValue()); } break; case SND_SEQ_EVENT_PGMCHANGE: { const ProgramChangeEvent* p = static_cast(ev); if(m_out != nullptr && m_thruEnabled) { m_out->sendProgram(p->getChannel(), p->getValue()); } Q_EMIT m_inp->midiProgram(p->getChannel(), p->getValue()); } break; case SND_SEQ_EVENT_CHANPRESS: { const ChanPressEvent* n = static_cast(ev); if(m_out != nullptr && m_thruEnabled) { m_out->sendChannelPressure(n->getChannel(), n->getValue()); } Q_EMIT m_inp->midiChannelPressure(n->getChannel(), n->getValue()); } break; case SND_SEQ_EVENT_PITCHBEND: { const PitchBendEvent* n = static_cast(ev); if(m_out != nullptr && m_thruEnabled) { m_out->sendPitchBend(n->getChannel(), n->getValue()); } Q_EMIT m_inp->midiPitchBend(n->getChannel(), n->getValue()); } break; case SND_SEQ_EVENT_SYSEX: { const SysExEvent* n = static_cast(ev); QByteArray data(n->getData(), n->getLength()); if(m_out != nullptr && m_thruEnabled) { m_out->sendSysex(data); } Q_EMIT m_inp->midiSysex(data); } break; case SND_SEQ_EVENT_SYSTEM: { const SystemEvent* n = static_cast(ev); int status = (int) n->getRaw8(0); if(m_out != nullptr && m_thruEnabled) { m_out->sendSystemMsg(status); } if (status < 0xF7) Q_EMIT m_inp->midiSystemCommon(status); else if (status > 0xF7) Q_EMIT m_inp->midiSystemRealtime(status); } break; default: break; } delete ev; } }; ALSAMIDIInput::ALSAMIDIInput(QObject *parent) : MIDIInput(parent), d(new ALSAMIDIInputPrivate(this)) { } ALSAMIDIInput::~ALSAMIDIInput() { delete d; } void ALSAMIDIInput::initialize(QSettings* settings) { Q_UNUSED(settings) d->initialize(); } QString ALSAMIDIInput::backendName() { return QStringLiteral("ALSA"); } QString ALSAMIDIInput::publicName() { return d->m_publicName; } void ALSAMIDIInput::setPublicName(QString name) { d->setPublicName(name); } QList ALSAMIDIInput::connections(bool advanced) { d->reloadDeviceList(advanced); return d->m_inputDevices; } void ALSAMIDIInput::setExcludedConnections(QStringList conns) { d->m_excludedNames = conns; } void ALSAMIDIInput::open(const MIDIConnection& name) { //qDebug() << Q_FUNC_INFO; auto b = d->setSubscription(name); if (!b) { d->m_diagnostics << "failed subscription to " + name.first; } } void ALSAMIDIInput::close() { //qDebug() << Q_FUNC_INFO; d->clearSubscription(); d->uninitialize(); } MIDIConnection ALSAMIDIInput::currentConnection() { return d->m_currentInput; } void ALSAMIDIInput::setMIDIThruDevice(MIDIOutput *device) { d->m_out = device; } void ALSAMIDIInput::enableMIDIThru(bool enable) { d->m_thruEnabled = enable; } bool ALSAMIDIInput::isEnabledMIDIThru() { return d->m_thruEnabled && (d->m_out != nullptr); } QStringList ALSAMIDIInput::getDiagnostics() { return d->m_diagnostics; } bool ALSAMIDIInput::getStatus() { return d->m_status; } } // namespace rt } // namespace drumstick drumstick-2.9.0/library/rt-backends/alsa-out/0000755000175000017500000000000014541630232020121 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/alsa-out/alsa-out.pro0000644000175000017500000000064014541630232022370 0ustar pedropedroTEMPLATE = lib CONFIG += c++11 plugin static { CONFIG += staticlib create_prl } TARGET = drumstick-rt-alsa-out DESTDIR = ../../../build/lib/drumstick2 DEPENDPATH += . ../../include INCLUDEPATH += . ../../include include (../../../global.pri) QT -= gui HEADERS += alsamidioutput.h SOURCES += alsamidioutput.cpp LIBS += -L../../../build/lib \ -ldrumstick-rt \ -ldrumstick-alsa \ -lasound drumstick-2.9.0/library/rt-backends/alsa-out/CMakeLists.txt0000644000175000017500000000462214541630232022665 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-rt-alsa-out_QTOBJ_SRCS alsamidioutput.h ) set(drumstick-rt-alsa-out_SRCS alsamidioutput.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt-alsa-out_MOC_SRCS ${drumstick-rt-alsa-out_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp(drumstick-rt-alsa-out_MOC_SRCS ${drumstick-rt-alsa-out_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library(drumstick-rt-alsa-out STATIC ${drumstick-rt-alsa-out_MOC_SRCS} ${drumstick-rt-alsa-out_SRCS}) target_compile_definitions(drumstick-rt-alsa-out PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-alsa-out PROPERTIES STATIC_LIB "libdrumstick-rt-alsa-out") else() add_library(drumstick-rt-alsa-out MODULE ${drumstick-rt-alsa-out_MOC_SRCS} ${drumstick-rt-alsa-out_SRCS} ) target_compile_definitions(drumstick-rt-alsa-out PRIVATE QT_PLUGIN) endif() target_link_libraries(drumstick-rt-alsa-out PRIVATE Qt${QT_VERSION_MAJOR}::Core Drumstick::ALSA Drumstick::RT ) set_target_properties(drumstick-rt-alsa-out PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install(TARGETS drumstick-rt-alsa-out EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR}) drumstick-2.9.0/library/rt-backends/alsa-out/alsamidioutput.cpp0000644000175000017500000002361514541630232023700 0ustar pedropedro/* Drumstick RT Backend using the ALSA Sequencer Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "alsamidioutput.h" #include #include #include #include #include #include #include #include namespace drumstick { namespace rt { using namespace ALSA; const QString ALSAMIDIOutput::DEFAULT_PUBLIC_NAME = QStringLiteral("MIDI Out"); class ALSAMIDIOutput::ALSAMIDIOutputPrivate { public: ALSAMIDIOutput *m_out; MidiClient *m_client; MidiPort *m_port; int m_portId; bool m_clientFilter; int m_runtimeAlsaNum; QString m_publicName; MIDIConnection m_currentOutput; QList m_outputDevices; QStringList m_excludedNames; QMutex m_outMutex; bool m_initialized; bool m_status; QStringList m_diagnostics; explicit ALSAMIDIOutputPrivate(ALSAMIDIOutput *q): m_out(q), m_client(nullptr), m_port(nullptr), m_portId(0), m_clientFilter(true), m_runtimeAlsaNum(0), m_publicName(DEFAULT_PUBLIC_NAME), m_initialized(false), m_status(false) { m_runtimeAlsaNum = getRuntimeALSALibraryNumber(); m_diagnostics.clear(); } ~ALSAMIDIOutputPrivate() { if (m_initialized) { clearSubscription(); uninitialize(); } } void initialize() { //qDebug() << Q_FUNC_INFO << m_initialized; if (!m_initialized) { m_client = new MidiClient(m_out); m_client->open(); m_client->setClientName(m_publicName); m_port = m_client->createPort(); m_port->setPortName("out"); m_port->setCapability( SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ ); m_port->setPortType( SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_MIDI_GENERIC ); m_portId = m_port->getPortId(); m_initialized = true; m_status = true; m_diagnostics.clear(); } } void uninitialize() { //qDebug() << Q_FUNC_INFO << m_initialized; if (m_initialized) { if (m_port != nullptr) { m_port->detach(); delete m_port; m_port = nullptr; } if (m_client != nullptr) { m_client->close(); delete m_client; m_client = nullptr; } m_initialized = false; m_status = false; m_diagnostics.clear(); } } bool clientIsAdvanced(int clientId) { // asking for runtime version instead of SND_LIB_VERSION if (m_runtimeAlsaNum < 0x01000B) // ALSA <= 1.0.10 return (clientId < 64); else // ALSA >= 1.0.11 return (clientId < 16); } void reloadDeviceList(bool advanced) { QStringList clientNames; QMap namesMap; if (!m_initialized) { initialize(); } auto outputs = m_client->getAvailableOutputs(); m_clientFilter = !advanced; m_outputDevices.clear(); for (const PortInfo &p : std::as_const(outputs)) { QString name = p.getClientName(); clientNames << name; if (namesMap.contains(name)) { namesMap[name]++; } else { namesMap.insert(name, 1); } } for (PortInfo& p : outputs) { bool excluded = false; QString name = p.getClientName(); if (m_clientFilter && clientIsAdvanced(p.getClient())) continue; if ( m_clientFilter && name.startsWith(QStringLiteral("Virtual Raw MIDI")) ) continue; if ( name.startsWith(m_publicName) ) continue; for (const QString &n : std::as_const(m_excludedNames)) { if ( name.startsWith(n) ) { excluded = true; break; } } if (!excluded) { int k = clientNames.count(name) + 1; QString addr = QString("%1:%2").arg(p.getClient()).arg(p.getPort()); if (k > 2) { m_outputDevices << MIDIConnection(QString("%1 (%2)").arg(name).arg(k - namesMap[name]--), addr); } else { m_outputDevices << MIDIConnection(name, addr); } } } if (!m_currentOutput.first.isEmpty() && !m_outputDevices.contains(m_currentOutput)) { m_currentOutput = MIDIConnection(); } } bool setSubscription(const MIDIConnection &newOutputDevice) { if (!m_initialized) { initialize(); } if (m_outputDevices.contains(newOutputDevice)) { m_currentOutput = newOutputDevice; m_port->unsubscribeAll(); m_port->subscribeTo(newOutputDevice.second.toString()); return true; } return false; } void clearSubscription() { if (!m_currentOutput.first.isEmpty() && m_initialized) { m_port->unsubscribeAll(); m_currentOutput = MIDIConnection(); } } void sendEvent(SequencerEvent *ev) { if (!m_initialized) { initialize(); } QMutexLocker locker(&m_outMutex); ev->setSource(m_portId); ev->setSubscribers(); ev->setDirect(); m_client->outputDirect(ev); } void setPublicName(QString newName) { if (newName != m_publicName) { m_publicName = newName; if (m_initialized) { m_client->setClientName(newName); } } } }; ALSAMIDIOutput::ALSAMIDIOutput(QObject *parent) : MIDIOutput(parent), d(new ALSAMIDIOutputPrivate(this)) { } ALSAMIDIOutput::~ALSAMIDIOutput() { delete d; } void ALSAMIDIOutput::initialize(QSettings* settings) { Q_UNUSED(settings) d->initialize(); } /* SLOTS */ void ALSAMIDIOutput::sendNoteOn(int chan, int note, int vel) { NoteOnEvent ev(chan, note, vel); d->sendEvent(&ev); } void ALSAMIDIOutput::sendNoteOff(int chan, int note, int vel) { NoteOffEvent ev(chan, note, vel); d->sendEvent(&ev); } void ALSAMIDIOutput::sendController(int chan, int control, int value) { ControllerEvent ev(chan, control, value); d->sendEvent(&ev); } void ALSAMIDIOutput::sendKeyPressure(int chan, int note, int value) { KeyPressEvent ev(chan, note, value); d->sendEvent(&ev); } void ALSAMIDIOutput::sendProgram(int chan, int program) { ProgramChangeEvent ev(chan, program); d->sendEvent(&ev); } void ALSAMIDIOutput::sendChannelPressure(int chan, int value) { ChanPressEvent ev(chan, value); d->sendEvent(&ev); } void ALSAMIDIOutput::sendPitchBend(int chan, int value) { PitchBendEvent ev(chan, value); d->sendEvent(&ev); } void ALSAMIDIOutput::sendSysex(const QByteArray& data) { SysExEvent ev(data); d->sendEvent(&ev); } void ALSAMIDIOutput::sendSystemMsg(const int status) { SystemEvent ev(status); d->sendEvent(&ev); } QStringList ALSAMIDIOutput::getDiagnostics() { return d->m_diagnostics; } bool ALSAMIDIOutput::getStatus() { return d->m_status; } QString ALSAMIDIOutput::backendName() { return "ALSA"; } QString ALSAMIDIOutput::publicName() { return d->m_publicName; } void ALSAMIDIOutput::setPublicName(QString name) { d->setPublicName(name); } QList ALSAMIDIOutput::connections(bool advanced) { d->reloadDeviceList(advanced); return d->m_outputDevices; } void ALSAMIDIOutput::setExcludedConnections(QStringList conns) { d->m_excludedNames = conns; } MIDIConnection ALSAMIDIOutput::currentConnection() { return d->m_currentOutput; } void ALSAMIDIOutput::open(const MIDIConnection& name) { bool b = d->setSubscription(name); if (!b) { d->m_diagnostics << "failed subscription to " + name.first; } } void ALSAMIDIOutput::close() { d->clearSubscription(); d->uninitialize(); } } // namespace rt } // namespace drumstick drumstick-2.9.0/library/rt-backends/alsa-out/alsamidioutput.h0000644000175000017500000000533114541630232023340 0ustar pedropedro/* Drumstick RT Backend using the ALSA Sequencer Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ALSAMIDIOUTPUT_H #define ALSAMIDIOUTPUT_H #include #include namespace drumstick { namespace rt { class ALSAMIDIOutput : public MIDIOutput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIOutput/2.0") Q_INTERFACES(drumstick::rt::MIDIOutput) Q_PROPERTY(QStringList diagnostics READ getDiagnostics) Q_PROPERTY(bool status READ getStatus) public: explicit ALSAMIDIOutput(QObject *parent = nullptr); virtual ~ALSAMIDIOutput(); virtual void initialize(QSettings* settings) override; virtual QString backendName() override; virtual QString publicName() override; virtual void setPublicName(QString name) override; virtual QList connections(bool advanced) override; virtual void setExcludedConnections(QStringList conns) override; virtual void open(const MIDIConnection& name) override; virtual void close() override; virtual MIDIConnection currentConnection() override; static const QString DEFAULT_PUBLIC_NAME; public Q_SLOTS: virtual void sendNoteOn(int chan, int note, int vel) override; virtual void sendNoteOff(int chan, int note, int vel) override; virtual void sendController(int chan, int control, int value) override; virtual void sendKeyPressure(int chan, int note, int value) override; virtual void sendProgram(int chan, int program) override; virtual void sendChannelPressure(int chan, int value) override; virtual void sendPitchBend(int chan, int value) override; virtual void sendSysex(const QByteArray& data) override; virtual void sendSystemMsg(const int status) override; private: class ALSAMIDIOutputPrivate; ALSAMIDIOutputPrivate *d; private: QStringList getDiagnostics(); bool getStatus(); }; }} #endif /* ALSAMIDIOUTPUT_H */ drumstick-2.9.0/library/rt-backends/common/0000755000175000017500000000000014541630232017664 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/common/maccommon.cpp0000644000175000017500000000457514541630232022354 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "maccommon.h" #if QT_VERSION < QT_VERSION_CHECK(5,2,0) QString CFStringToQString(CFStringRef str) { if (!str) return QString(); CFIndex length = CFStringGetLength(str); const UniChar *chars = CFStringGetCharactersPtr(str); if (chars) return QString(reinterpret_cast(chars), length); QVarLengthArray buffer(length); CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data()); return QString(reinterpret_cast(buffer.constData()), length); } #endif QString getEndpointName(MIDIEndpointRef endpoint) { QString result; CFStringRef str = 0; MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &str); if (str != 0) { result = QString::fromCFString(str); CFRelease(str); str = 0; } MIDIEntityRef entity = 0; MIDIEndpointGetEntity(endpoint, &entity); if (entity == 0) return result; if (result.isEmpty()) { MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str); if (str != 0) { result = QString::fromCFString(str); CFRelease(str); str = 0; } } MIDIDeviceRef device = 0; MIDIEntityGetDevice (entity, &device); if (device == 0) return result; MIDIObjectGetStringProperty (device, kMIDIPropertyName, &str); if (str != 0) { QString s = QString::fromCFString(str); CFRelease (str); str = 0; if (!result.startsWith(s, Qt::CaseInsensitive) ) result = (s + ' ' + result).trimmed(); } return result; } drumstick-2.9.0/library/rt-backends/common/maccommon.h0000644000175000017500000000207214541630232022007 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef MACCOMMON_H #define MACCOMMON_H #include #include #if QT_VERSION < QT_VERSION_CHECK(5,2,0) QString CFStringToQString(CFStringRef str); #endif QString getEndpointName(MIDIEndpointRef endpoint); QString getErrorTextFromOSStatus(OSStatus err); #endif // MACCOMMON_H drumstick-2.9.0/library/rt-backends/common/maccommon.mm0000644000175000017500000000176714541630232022203 0ustar pedropedro/* Drumstick RT Mac OSX Backend Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "maccommon.h" #import QString getErrorTextFromOSStatus(OSStatus err) { NSError* error = [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil]; return QString::fromNSString( error.description ); } drumstick-2.9.0/library/rt-backends/common/midiparser.cpp0000644000175000017500000002102714541630232022531 0ustar pedropedro/* Drumstick MIDI realtime input-output Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "midiparser.h" #include namespace drumstick { namespace rt { class MIDIParser::MIDIParserPrivate { public: MIDIParserPrivate(): m_in(nullptr), m_out(nullptr), m_running_status(0) { } MIDIInput *m_in; MIDIOutput *m_out; unsigned char m_running_status; QByteArray m_buffer; void processNoteOff(const int chan, const int note, const int vel) { //qDebug() << "NoteOff(" << hex << chan << "," << note << "," << vel << ")"; if (m_in != nullptr && m_in->isEnabledMIDIThru() && m_out != nullptr) { m_out->sendNoteOff(chan, note, vel); } if (m_in != nullptr) { Q_EMIT m_in->midiNoteOff(chan, note, vel); } } void processNoteOn(const int chan, const int note, const int vel) { //qDebug() << "NoteOn(" << hex << chan << "," << note << "," << vel << ")"; if (m_in != nullptr && m_in->isEnabledMIDIThru() && m_out != nullptr) { m_out->sendNoteOn(chan, note, vel); } if (m_in != nullptr) { Q_EMIT m_in->midiNoteOn(chan, note, vel); } } void processKeyPressure(const int chan, const int note, const int value) { //qDebug() << "KeyPressure(" << hex << chan << "," << note << "," << value << ")"; if (m_in != nullptr && m_in->isEnabledMIDIThru() && m_out != nullptr) { m_out->sendKeyPressure(chan, note, value); } if (m_in != nullptr) { Q_EMIT m_in->midiKeyPressure(chan, note, value); } } void processController(const int chan, const int control, const int value) { //qDebug() << "Controller(" << chan << "," << control << "," << value << ")"; if (m_in != nullptr && m_in->isEnabledMIDIThru() && m_out != nullptr) { m_out->sendController(chan, control, value); } if (m_in != nullptr) { Q_EMIT m_in->midiController(chan, control, value); } } void processProgram(const int chan, const int program) { //qDebug() << "Program(" << hex << chan << "," << program << ")"; if (m_in != nullptr && m_in->isEnabledMIDIThru() && m_out != nullptr) { m_out->sendProgram(chan, program); } if (m_in != nullptr) { Q_EMIT m_in->midiProgram(chan, program); } } void processChannelPressure(const int chan, const int value) { //qDebug() << "ChannelPressure(" << chan << "," << value << ")"; if (m_in != nullptr && m_in->isEnabledMIDIThru() && m_out != nullptr) { m_out->sendChannelPressure(chan, value); } if (m_in != nullptr) { Q_EMIT m_in->midiChannelPressure(chan, value); } } void processPitchBend(const int chan, const int value) { //qDebug() << "PitchBend(" << chan << "," << value << ")"; if (m_in != nullptr && m_in->isEnabledMIDIThru() && m_out != nullptr) { m_out->sendPitchBend(chan, value); } if (m_in != nullptr) { Q_EMIT m_in->midiPitchBend(chan, value); } } void processSysex(const QByteArray &data) { //qDebug() << "Sysex(" << data.toHex() << ")"; if (m_in != nullptr && m_in->isEnabledMIDIThru() && m_out != nullptr) { m_out->sendSysex(data); } if (m_in != nullptr) { Q_EMIT m_in->midiSysex(data); } } void processSystemCommon(const int status) { //qDebug() << "common SystemMsg(" << hex << status << ")"; if (m_in != nullptr && m_in->isEnabledMIDIThru() && m_out != nullptr) { m_out->sendSystemMsg(status); } if (m_in != nullptr) { Q_EMIT m_in->midiSystemCommon(status); } } void processSystemRealtime(unsigned char byte) { //qDebug() << "realtime SystemMsg(" << hex << byte << ")"; if (m_in != nullptr && m_in->isEnabledMIDIThru() && m_out != nullptr) { m_out->sendSystemMsg(byte); } if (m_in != nullptr) { Q_EMIT m_in->midiSystemRealtime(byte); } } }; MIDIParser::MIDIParser(MIDIInput *in, QObject *parent) : QObject(parent), d(new MIDIParser::MIDIParserPrivate) { d->m_buffer.clear(); d->m_in = in; } MIDIParser::~MIDIParser() { delete d; } void MIDIParser::setMIDIThruDevice(MIDIOutput *device) { d->m_out = device; } void MIDIParser::parse(unsigned char byte) { if (byte >= MIDI_STATUS_REALTIME) { // system realtime d->processSystemRealtime(byte); return; } else d->m_buffer.append(byte); while(d->m_buffer.length() > 0) { int chan, m1, m2, v; unsigned char status = static_cast(d->m_buffer.at(0)); if (status == MIDI_STATUS_SYSEX) { // system exclusive if (byte == MIDI_STATUS_ENDSYSEX) { d->processSysex(d->m_buffer); d->m_buffer.clear(); } else return; } else if (status > MIDI_STATUS_SYSEX && status < MIDI_STATUS_ENDSYSEX) { // system common d->processSystemCommon(status); d->m_buffer.clear(); } else if (status < MIDI_STATUS_SYSEX && status >= MIDI_STATUS_NOTEOFF) { // channel message d->m_running_status = status; chan = status & MIDI_CHANNEL_MASK; status = status & MIDI_STATUS_MASK; switch(status) { case MIDI_STATUS_NOTEOFF: if (d->m_buffer.length() < 3) return; m1 = static_cast(d->m_buffer.at(1)); m2 = static_cast(d->m_buffer.at(2)); d->processNoteOff(chan, m1, m2); break; case MIDI_STATUS_NOTEON: if (d->m_buffer.length() < 3) return; m1 = static_cast(d->m_buffer.at(1)); m2 = static_cast(d->m_buffer.at(2)); d->processNoteOn(chan, m1, m2); break; case MIDI_STATUS_KEYPRESURE: if (d->m_buffer.length() < 3) return; m1 = static_cast(d->m_buffer.at(1)); m2 = static_cast(d->m_buffer.at(2)); d->processKeyPressure(chan, m1, m2); break; case MIDI_STATUS_CONTROLCHANGE: if (d->m_buffer.length() < 3) return; m1 = static_cast(d->m_buffer.at(1)); m2 = static_cast(d->m_buffer.at(2)); d->processController(chan, m1, m2); break; case MIDI_STATUS_PROGRAMCHANGE: if (d->m_buffer.length() < 2) return; m1 = static_cast(d->m_buffer.at(1)); d->processProgram(chan, m1); break; case MIDI_STATUS_CHANNELPRESSURE: if (d->m_buffer.length() < 2) return; m1 = static_cast(d->m_buffer.at(1)); d->processChannelPressure(chan, m1); break; case MIDI_STATUS_PITCHBEND: if (d->m_buffer.length() < 3) return; m1 = static_cast(d->m_buffer.at(1)); m2 = static_cast(d->m_buffer.at(2)); v = m1 + m2 * 0x80 - 0x2000; d->processPitchBend(chan, v); break; } d->m_buffer.clear(); } else { // running status d->m_buffer.insert(0, d->m_running_status); } } } void MIDIParser::parse(QByteArray bytes) { foreach(unsigned char byte, bytes) { parse(byte); } } } // namespace rt } // namespace drumstick drumstick-2.9.0/library/rt-backends/common/midiparser.h0000644000175000017500000000242314541630232022175 0ustar pedropedro/* Drumstick MIDI realtime input-output Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef MIDIPARSER_H #define MIDIPARSER_H #include #include namespace drumstick { namespace rt { class MIDIParser : public QObject { Q_OBJECT public: explicit MIDIParser(MIDIInput *in = nullptr, QObject *parent = nullptr); virtual ~MIDIParser(); void setMIDIThruDevice(MIDIOutput* device); public Q_SLOTS: void parse(unsigned char byte); void parse(QByteArray bytes); private: class MIDIParserPrivate; MIDIParserPrivate *d; }; }} #endif // MIDIPARSER_H drumstick-2.9.0/library/rt-backends/dummy-in/0000755000175000017500000000000014541630232020133 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/dummy-in/CMakeLists.txt0000644000175000017500000000470014541630232022674 0ustar pedropedro# MIDI Sequencer C++ Library # Copyright (C) 2005-2023 Pedro Lopez-Cabanillas # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set(CMAKE_INCLUDE_CURRENT_DIR ON) find_package(Qt${QT_VERSION_MAJOR}Widgets REQUIRED) set(drumstick-rt-dummy-in_QTOBJ_SRCS dummyinput.h ) set(drumstick-rt-dummy-in_SRCS dummyinput.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt-dummy-in_MOC_SRCS ${drumstick-rt-dummy-in_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp(drumstick-rt-dummy-in_MOC_SRCS ${drumstick-rt-dummy-in_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library(drumstick-rt-dummy-in STATIC ${drumstick-rt-dummy-in_MOC_SRCS} ${drumstick-rt-dummy-in_SRCS} ) target_compile_definitions(drumstick-rt-dummy-in PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-dummy-in PROPERTIES STATIC_LIB "libdrumstick-rt-dummy-in") else() add_library(drumstick-rt-dummy-in MODULE ${drumstick-rt-dummy-in_MOC_SRCS} ${drumstick-rt-dummy-in_SRCS} ) target_compile_definitions(drumstick-rt-dummy-in PRIVATE QT_PLUGIN) endif() target_link_libraries(drumstick-rt-dummy-in PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Widgets Drumstick::RT ) target_include_directories(drumstick-rt-dummy-in PRIVATE ${Drumstick_SOURCE_DIR}/library/include ) set_target_properties(drumstick-rt-dummy-in PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install(TARGETS drumstick-rt-dummy-in EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR}) drumstick-2.9.0/library/rt-backends/dummy-in/dummy-in.pro0000644000175000017500000000056514541630232022422 0ustar pedropedroTEMPLATE = lib TARGET = drumstick-rt-dummy-in DESTDIR = ../../../build/lib/drumstick2 include (../../../global.pri) CONFIG += c++11 plugin static { CONFIG += staticlib create_prl } DEPENDPATH += ../../include INCLUDEPATH += ../../include QT -= gui HEADERS += dummyinput.h SOURCES += dummyinput.cpp LIBS += -L$$OUT_PWD/../../../build/lib -l$$drumstickLib(drumstick-rt) drumstick-2.9.0/library/rt-backends/dummy-in/dummyinput.cpp0000644000175000017500000000445314541630232023060 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "dummyinput.h" namespace drumstick { namespace rt { void DummyInput::initialize(QSettings* settings) { Q_UNUSED(settings) } QString DummyInput::backendName() { return QStringLiteral("DummyInput"); } QString DummyInput::publicName() { return QStringLiteral("DummyInput"); } void DummyInput::setPublicName(QString name) { Q_UNUSED(name) } QList DummyInput::connections(bool advanced) { Q_UNUSED(advanced) return QList(); } void DummyInput::setExcludedConnections(QStringList conns) { Q_UNUSED(conns) } MIDIConnection DummyInput::currentConnection() { return MIDIConnection(); } void DummyInput::open(const MIDIConnection& conn) { Q_UNUSED(conn) } void DummyInput::close() { } void DummyInput::setMIDIThruDevice(MIDIOutput *device) { Q_UNUSED(device) } void DummyInput::enableMIDIThru(bool enable) { Q_UNUSED(enable) } bool DummyInput::isEnabledMIDIThru() { return false; } bool DummyInput::configure(QWidget *parent) { return QMessageBox::Ok == QMessageBox::information(parent, "Hello Configuration", "Hello world configuration dialog!", QMessageBox::Ok | QMessageBox::Cancel); } QStringList DummyInput::getDiagnostics() { return QStringList(); } QString DummyInput::getLibVersion() { return QT_STRINGIFY(VERSION); } bool DummyInput::getStatus() { return true; } bool DummyInput::getConfigurable() { return true; } }} drumstick-2.9.0/library/rt-backends/dummy-in/dummyinput.h0000644000175000017500000000444314541630232022524 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DUMMYINPUT_H #define DUMMYINPUT_H #include #include namespace drumstick { namespace rt { class DummyInput : public MIDIInput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIInput/2.0") Q_INTERFACES(drumstick::rt::MIDIInput) Q_PROPERTY(QStringList diagnostics READ getDiagnostics) Q_PROPERTY(QString libversion READ getLibVersion) Q_PROPERTY(bool status READ getStatus) Q_PROPERTY(bool isconfigurable READ getConfigurable) public: explicit DummyInput(QObject *parent = nullptr) : MIDIInput(parent) {} virtual ~DummyInput() = default; // MIDIInput interface public: virtual void initialize(QSettings* settings); virtual QString backendName(); virtual QString publicName(); virtual void setPublicName(QString name); virtual QList connections(bool advanced); virtual void setExcludedConnections(QStringList conns); virtual void open(const MIDIConnection& name); virtual void close(); virtual MIDIConnection currentConnection(); virtual void setMIDIThruDevice(MIDIOutput *device); virtual void enableMIDIThru(bool enable); virtual bool isEnabledMIDIThru(); public Q_SLOTS: bool configure(QWidget *parent); private: QStringList getDiagnostics(); QString getLibVersion(); bool getStatus(); bool getConfigurable(); }; }} #endif // DUMMYINPUT_H drumstick-2.9.0/library/rt-backends/dummy-out/0000755000175000017500000000000014541630232020334 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/dummy-out/CMakeLists.txt0000644000175000017500000000471414541630232023102 0ustar pedropedro# MIDI Sequencer C++ Library # Copyright (C) 2005-2023 Pedro Lopez-Cabanillas # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set(CMAKE_INCLUDE_CURRENT_DIR ON) find_package(Qt${QT_VERSION_MAJOR}Widgets REQUIRED) set(drumstick-rt-dummy-out_QTOBJ_SRCS dummyoutput.h ) set(drumstick-rt-dummy-out_SRCS dummyoutput.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt-dummy-out_MOC_SRCS ${drumstick-rt-dummy-out_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp(drumstick-rt-dummy-out_MOC_SRCS ${drumstick-rt-dummy-out_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library(drumstick-rt-dummy-out STATIC ${drumstick-rt-dummy-out_MOC_SRCS} ${drumstick-rt-dummy-out_SRCS}) target_compile_definitions(drumstick-rt-dummy-out PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-dummy-out PROPERTIES STATIC_LIB "libdrumstick-rt-dummy-out") else() add_library(drumstick-rt-dummy-out MODULE ${drumstick-rt-dummy-out_MOC_SRCS} ${drumstick-rt-dummy-out_SRCS}) target_compile_definitions(drumstick-rt-dummy-out PRIVATE QT_PLUGIN) endif() target_link_libraries(drumstick-rt-dummy-out PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Widgets Drumstick::RT ) target_include_directories(drumstick-rt-dummy-out PRIVATE ${Drumstick_SOURCE_DIR}/library/include ) set_target_properties(drumstick-rt-dummy-out PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install(TARGETS drumstick-rt-dummy-out EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR}) drumstick-2.9.0/library/rt-backends/dummy-out/dummy-out.pro0000644000175000017500000000057014541630232023020 0ustar pedropedroTEMPLATE = lib TARGET = drumstick-rt-dummy-out DESTDIR = ../../../build/lib/drumstick2 include (../../../global.pri) CONFIG += c++11 plugin static { CONFIG += staticlib create_prl } DEPENDPATH += ../../include INCLUDEPATH += ../../include QT -= gui HEADERS += dummyoutput.h SOURCES += dummyoutput.cpp LIBS += -L$$OUT_PWD/../../../build/lib -l$$drumstickLib(drumstick-rt) drumstick-2.9.0/library/rt-backends/dummy-out/dummyoutput.cpp0000644000175000017500000000603714541630232023462 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "dummyoutput.h" namespace drumstick { namespace rt { void DummyOutput::initialize(QSettings* settings) { Q_UNUSED(settings) } QString DummyOutput::backendName() { return QStringLiteral("DummyOutput"); } QString DummyOutput::publicName() { return QStringLiteral("DummyOutput"); } void DummyOutput::setPublicName(QString name) { Q_UNUSED(name) } QList DummyOutput::connections(bool advanced) { Q_UNUSED(advanced) return QList(); } void DummyOutput::setExcludedConnections(QStringList conns) { Q_UNUSED(conns) } void DummyOutput::open(const MIDIConnection& conn) { Q_UNUSED(conn) } void DummyOutput::close() { } MIDIConnection DummyOutput::currentConnection() { return MIDIConnection(); } void DummyOutput::sendNoteOff(int chan, int note, int vel) { Q_UNUSED(chan) Q_UNUSED(note) Q_UNUSED(vel) } void DummyOutput::sendNoteOn(int chan, int note, int vel) { Q_UNUSED(chan) Q_UNUSED(note) Q_UNUSED(vel) } void DummyOutput::sendKeyPressure(int chan, int note, int value) { Q_UNUSED(chan) Q_UNUSED(note) Q_UNUSED(value) } void DummyOutput::sendController(int chan, int control, int value) { Q_UNUSED(chan) Q_UNUSED(control) Q_UNUSED(value) } void DummyOutput::sendProgram(int chan, int program) { Q_UNUSED(chan) Q_UNUSED(program) } void DummyOutput::sendChannelPressure(int chan, int value) { Q_UNUSED(chan) Q_UNUSED(value) } void DummyOutput::sendPitchBend(int chan, int value) { Q_UNUSED(chan) Q_UNUSED(value) } void DummyOutput::sendSysex(const QByteArray &data) { Q_UNUSED(data) } void DummyOutput::sendSystemMsg(const int status) { Q_UNUSED(status) } bool DummyOutput::configure(QWidget *parent) { return QMessageBox::Ok == QMessageBox::information(parent, "Hello Configuration", "Hello world configuration dialog!", QMessageBox::Ok | QMessageBox::Cancel); } QStringList DummyOutput::getDiagnostics() { return QStringList(); } QString DummyOutput::getLibVersion() { return QT_STRINGIFY(VERSION); } bool DummyOutput::getStatus() { return true; } bool DummyOutput::getConfigurable() { return true; } }} drumstick-2.9.0/library/rt-backends/dummy-out/dummyoutput.h0000644000175000017500000000527714541630232023134 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DUMMYOUTPUT_H #define DUMMYOUTPUT_H #include #include namespace drumstick { namespace rt { class DummyOutput : public MIDIOutput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIOutput/2.0") Q_INTERFACES(drumstick::rt::MIDIOutput) Q_PROPERTY(QStringList diagnostics READ getDiagnostics) Q_PROPERTY(QString libversion READ getLibVersion) Q_PROPERTY(bool status READ getStatus) Q_PROPERTY(bool isconfigurable READ getConfigurable) public: explicit DummyOutput(QObject *parent = nullptr) : MIDIOutput(parent) {} virtual ~DummyOutput() = default; // MIDIOutput interface public: virtual void initialize(QSettings* settings); virtual QString backendName(); virtual QString publicName(); virtual void setPublicName(QString name); virtual QList connections(bool advanced); virtual void setExcludedConnections(QStringList conns); virtual void open(const MIDIConnection& name); virtual void close(); virtual MIDIConnection currentConnection(); public Q_SLOTS: virtual void sendNoteOff(int chan, int note, int vel); virtual void sendNoteOn(int chan, int note, int vel); virtual void sendKeyPressure(int chan, int note, int value); virtual void sendController(int chan, int control, int value); virtual void sendProgram(int chan, int program); virtual void sendChannelPressure(int chan, int value); virtual void sendPitchBend(int chan, int value); virtual void sendSysex(const QByteArray &data); virtual void sendSystemMsg(const int status); bool configure(QWidget *parent); private: QStringList getDiagnostics(); QString getLibVersion(); bool getStatus(); bool getConfigurable(); }; }} #endif // DUMMYOUTPUT_H drumstick-2.9.0/library/rt-backends/eassynth/0000755000175000017500000000000014541630232020232 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/eassynth/eassynth.pro0000644000175000017500000000120314541630232022606 0ustar pedropedroTEMPLATE = lib TARGET = drumstick-rt-eassynth DESTDIR = ../../../build/lib/drumstick2 include (../../../global.pri) CONFIG += c++11 plugin link_prl static { CONFIG += staticlib create_prl } DEPENDPATH += . ../../include INCLUDEPATH += . ../../include QT -= gui LIBS += -L$$OUT_PWD/../../../build/lib -ldrumstick-rt HEADERS += synthcontroller.h \ synthrenderer.h \ filewrapper.h SOURCES += synthcontroller.cpp \ synthrenderer.cpp \ filewrapper.cpp CONFIG += link_pkgconfig packagesExist(libpulse-simple) { PKGCONFIG += libpulse-simple } packagesExist(sonivox) { PKGCONFIG += sonivox } drumstick-2.9.0/library/rt-backends/eassynth/filewrapper.h0000644000175000017500000000236314541630232022727 0ustar pedropedro/* Sonivox EAS Synthesizer for Qt applications Copyright (C) 2016-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef FILEWRAPPER_H #define FILEWRAPPER_H #include #include #include class FileWrapper { public: FileWrapper(const QString& path); FileWrapper(const char *path); ~FileWrapper(); EAS_FILE_LOCATOR getLocator(); int readAt(void *buffer, int offset, int size); int size(); bool ok() const; private: bool m_ok; off64_t m_Base; int64_t m_Length; EAS_FILE m_easFile; QFile m_file; }; #endif // FILEWRAPPER_H drumstick-2.9.0/library/rt-backends/eassynth/CMakeLists.txt0000644000175000017500000000463314541630232023000 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DQT_NO_DEBUG_OUTPUT") set( HEADERS synthcontroller.h synthrenderer.h ) set( SOURCES synthcontroller.cpp synthrenderer.cpp filewrapper.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp( MOC_SRCS ${HEADERS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp( MOC_SRCS ${HEADERS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library( drumstick-rt-eassynth STATIC ${MOC_SRCS} ${SOURCES} ${HEADERS}) target_compile_definitions(drumstick-rt-eassynth PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-eassynth PROPERTIES STATIC_LIB "libdrumstick-rt-eassynth") else() add_library( drumstick-rt-eassynth MODULE ${MOC_SRCS} ${SOURCES} ${HEADERS}) target_compile_definitions(drumstick-rt-eassynth PRIVATE QT_PLUGIN) endif() target_link_libraries( drumstick-rt-eassynth PRIVATE Qt${QT_VERSION_MAJOR}::Core PkgConfig::PULSE sonivox::sonivox Drumstick::RT ) target_include_directories(drumstick-rt-eassynth PRIVATE ${Drumstick_SOURCE_DIR}/library/include ) set_target_properties(drumstick-rt-eassynth PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install( TARGETS drumstick-rt-eassynth EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} ) drumstick-2.9.0/library/rt-backends/eassynth/filewrapper.cpp0000644000175000017500000000423614541630232023263 0ustar pedropedro/* Sonivox EAS Synthesizer for Qt applications Copyright (C) 2016-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "filewrapper.h" static int readAt(void *handle, void *buffer, int pos, int size) { return static_cast(handle)->readAt(buffer, pos, size); } static int size(void *handle) { return static_cast(handle)->size(); } FileWrapper::FileWrapper(const QString &path) : m_ok{false} , m_Base{0} , m_Length{0} , m_easFile{} { //qDebug() << Q_FUNC_INFO << path; m_file.setFileName(path); m_ok = m_file.open(QIODevice::ReadOnly); if (m_ok) { //qDebug("FileWrapper. opened %s", path); m_Length = m_file.size(); m_easFile.handle = this; m_easFile.readAt = ::readAt; m_easFile.size = ::size; } } FileWrapper::FileWrapper(const char *path) : FileWrapper(QString::fromLocal8Bit(path)) { //qDebug("FileWrapper(path=%s)", path); } FileWrapper::~FileWrapper() { //qDebug("~FileWrapper"); m_file.close(); } int FileWrapper::readAt(void *buffer, int offset, int size) { //qDebug("readAt(%p, %d, %d)", buffer, offset, size); m_file.seek(offset); if (offset + size > m_Length) { size = m_Length - offset; } return m_file.read((char *)buffer, size); } int FileWrapper::size() { //qDebug("size() = %d", int(mLength)); return m_Length; } bool FileWrapper::ok() const { return m_ok; } EAS_FILE_LOCATOR FileWrapper::getLocator() { return &m_easFile; } drumstick-2.9.0/library/rt-backends/eassynth/synthcontroller.cpp0000644000175000017500000001043414541630232024211 0ustar pedropedro/* Sonivox EAS Synthesizer for Qt applications Copyright (C) 2016-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "synthcontroller.h" #include "synthrenderer.h" namespace drumstick { namespace rt { SynthController::SynthController(QObject *parent) : MIDIOutput(parent) { m_renderer = new SynthRenderer(); m_renderer->moveToThread(&m_renderingThread); connect(&m_renderingThread, &QThread::started, m_renderer, &SynthRenderer::run); } SynthController::~SynthController() { //qDebug() << Q_FUNC_INFO; if (m_renderingThread.isRunning()) { stop(); } delete m_renderer; m_renderer = nullptr; } void SynthController::start() { QMutex mutex; mutex.lock(); m_renderer->setCondition(&m_rendering); m_renderingThread.start(QThread::HighPriority); m_rendering.wait(&mutex); mutex.unlock(); } void SynthController::stop() { //qDebug() << Q_FUNC_INFO; m_renderer->stop(); m_renderingThread.quit(); m_renderingThread.wait(); } void SynthController::initialize(QSettings* settings) { m_renderer->initialize(settings); //qDebug() << Q_FUNC_INFO; } QString SynthController::backendName() { return SynthRenderer::QSTR_SONIVOXEAS; } QString SynthController::publicName() { return SynthRenderer::QSTR_SONIVOXEAS; } void SynthController::setPublicName(QString name) { Q_UNUSED(name) } QList SynthController::connections(bool advanced) { Q_UNUSED(advanced) return QList{MIDIConnection(SynthRenderer::QSTR_SONIVOXEAS, SynthRenderer::QSTR_SONIVOXEAS)}; } void SynthController::setExcludedConnections(QStringList conns) { Q_UNUSED(conns) } void SynthController::open(const MIDIConnection& name) { Q_UNUSED(name) //qDebug() << Q_FUNC_INFO << name; start(); } void SynthController::close() { //qDebug() << Q_FUNC_INFO; stop(); } MIDIConnection SynthController::currentConnection() { return m_renderer->connection(); } void SynthController::sendNoteOff(int chan, int note, int vel) { m_renderer->sendMessage(MIDI_STATUS_NOTEOFF + chan, note, vel); } void SynthController::sendNoteOn(int chan, int note, int vel) { m_renderer->sendMessage(MIDI_STATUS_NOTEON + chan, note, vel); } void SynthController::sendKeyPressure(int chan, int note, int value) { m_renderer->sendMessage(MIDI_STATUS_KEYPRESURE + chan, note, value); } void SynthController::sendController(int chan, int control, int value) { m_renderer->sendMessage(MIDI_STATUS_CONTROLCHANGE + chan, control, value); } void SynthController::sendProgram(int chan, int program) { m_renderer->sendMessage(MIDI_STATUS_PROGRAMCHANGE + chan, program); } void SynthController::sendChannelPressure(int chan, int value) { m_renderer->sendMessage(MIDI_STATUS_CHANNELPRESSURE + chan, value); } void SynthController::sendPitchBend(int chan, int v) { // -8192 <= v <= 8191; 0 <= value <= 16384 int value = 8192 + v; m_renderer->sendMessage(MIDI_STATUS_PITCHBEND + chan, MIDI_LSB(value), MIDI_MSB(value)); } void SynthController::sendSysex(const QByteArray &data) { Q_UNUSED(data) } void SynthController::sendSystemMsg(const int status) { Q_UNUSED(status) } void SynthController::writeSettings(QSettings *settings) { m_renderer->writeSettings(settings); } QStringList SynthController::getDiagnostics() { return m_renderer->getDiagnostics(); } bool SynthController::getStatus() { return m_renderer->getStatus(); } QString SynthController::getLibVersion() { return m_renderer->getLibVersion(); } QString SynthController::getSoundFont() { return m_renderer->getSoundFont(); } } // namespace rt } // namespace drumstick drumstick-2.9.0/library/rt-backends/eassynth/synthcontroller.h0000644000175000017500000000611714541630232023661 0ustar pedropedro/* Sonivox EAS Synthesizer for Qt applications Copyright (C) 2016-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SYNTHCONTROLLER_H #define SYNTHCONTROLLER_H #include #include #include #include #include "synthrenderer.h" namespace drumstick { namespace rt { class SynthController : public MIDIOutput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIOutput/2.0") Q_INTERFACES(drumstick::rt::MIDIOutput) Q_PROPERTY(QStringList diagnostics READ getDiagnostics) Q_PROPERTY(bool status READ getStatus) Q_PROPERTY(QString libversion READ getLibVersion) Q_PROPERTY(QString soundfont READ getSoundFont) public: explicit SynthController(QObject *parent = nullptr); virtual ~SynthController(); void start(); void stop(); // MIDIOutput interface public: virtual void initialize(QSettings* settings) override; virtual QString backendName() override; virtual QString publicName() override; virtual void setPublicName(QString name) override; virtual QList connections(bool advanced) override; virtual void setExcludedConnections(QStringList conns) override; virtual void open(const MIDIConnection& name) override; virtual void close() override; virtual MIDIConnection currentConnection() override; public Q_SLOTS: virtual void sendNoteOff(int chan, int note, int vel) override; virtual void sendNoteOn(int chan, int note, int vel) override; virtual void sendKeyPressure(int chan, int note, int value) override; virtual void sendController(int chan, int control, int value) override; virtual void sendProgram(int chan, int program) override; virtual void sendChannelPressure(int chan, int value) override; virtual void sendPitchBend(int chan, int value) override; virtual void sendSysex(const QByteArray &data) override; virtual void sendSystemMsg(const int status) override; void writeSettings(QSettings *settings); private: QThread m_renderingThread; SynthRenderer *m_renderer; QWaitCondition m_rendering; private: QStringList getDiagnostics(); bool getStatus(); QString getLibVersion(); QString getSoundFont(); }; }} #endif // SYNTHCONTROLLER_H drumstick-2.9.0/library/rt-backends/eassynth/synthrenderer.cpp0000644000175000017500000003214014541630232023632 0ustar pedropedro/* Sonivox EAS Synthesizer for Qt applications Copyright (C) 2016-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "synthrenderer.h" #include "filewrapper.h" namespace drumstick { namespace rt { const QString SynthRenderer::QSTR_PREFERENCES = QStringLiteral("SonivoxEAS"); const QString SynthRenderer::QSTR_BUFFERTIME = QStringLiteral("BufferTime"); const QString SynthRenderer::QSTR_REVERBTYPE = QStringLiteral("ReverbType"); const QString SynthRenderer::QSTR_REVERBAMT = QStringLiteral("ReverbAmt"); const QString SynthRenderer::QSTR_CHORUSTYPE = QStringLiteral("ChorusType"); const QString SynthRenderer::QSTR_CHORUSAMT = QStringLiteral("ChorusAmt"); const QString SynthRenderer::QSTR_SONIVOXEAS = QStringLiteral("SonivoxEAS"); const QString SynthRenderer::QSTR_SOUNDFONT = QStringLiteral("InstrumentsDefinition"); const int SynthRenderer::DEF_BUFFERTIME = 60; const int SynthRenderer::DEF_REVERBTYPE = EAS_PARAM_REVERB_HALL; const int SynthRenderer::DEF_REVERBAMT = 25800; const int SynthRenderer::DEF_CHORUSTYPE = -1; const int SynthRenderer::DEF_CHORUSAMT = 0; SynthRenderer::SynthRenderer(QObject *parent) : QObject(parent), m_Stopped(true), m_rendering(nullptr), m_easData(nullptr), m_streamHandle(nullptr), m_bufferTime(60) { } void SynthRenderer::initEAS() { /* SONiVOX EAS initialization */ EAS_RESULT eas_res; EAS_DATA_HANDLE dataHandle; EAS_HANDLE handle; m_status = false; m_diagnostics.clear(); const S_EAS_LIB_CONFIG *easConfig = EAS_Config(); if (easConfig == nullptr) { m_diagnostics << "EAS_Config returned null"; return; } m_sampleRate = easConfig->sampleRate; m_bufferSize = easConfig->mixBufferSize; m_channels = easConfig->numChannels; m_libVersion = easConfig->libVersion; eas_res = EAS_Init(&dataHandle); if (eas_res != EAS_SUCCESS) { m_diagnostics << QString("EAS_Init error: %1").arg( eas_res ); return; } m_easData = dataHandle; if (!m_soundfont.isEmpty()) { FileWrapper dlsFile(m_soundfont); if (dlsFile.ok()) { eas_res = EAS_LoadDLSCollection(dataHandle, nullptr, dlsFile.getLocator()); if (eas_res != EAS_SUCCESS) { m_diagnostics << QString("EAS_LoadDLSCollection(%1) error: %2").arg(m_soundfont).arg(eas_res); } } else { m_diagnostics << QString("Failed to open %1").arg(m_soundfont); } } eas_res = EAS_OpenMIDIStream(dataHandle, &handle, nullptr); if (eas_res != EAS_SUCCESS) { m_diagnostics << QString("EAS_OpenMIDIStream error: %1").arg( eas_res ); EAS_Shutdown(dataHandle); return; } m_streamHandle = handle; Q_ASSERT(m_streamHandle != nullptr); m_status = true; //qDebug() << Q_FUNC_INFO << "EAS bufferSize=" << m_bufferSize << " sampleRate=" << m_sampleRate << " channels=" << m_channels; } void SynthRenderer::initPulse() { pa_sample_spec samplespec; pa_buffer_attr bufattr; int period_bytes; char *server = nullptr; char *device = nullptr; int err; samplespec.format = PA_SAMPLE_S16LE; samplespec.channels = m_channels; samplespec.rate = m_sampleRate; //period_bytes = m_bufferSize * sizeof (EAS_PCM) * m_channels; period_bytes = pa_usec_to_bytes(m_bufferTime * 1000, &samplespec); bufattr.maxlength = (int32_t)-1; bufattr.tlength = period_bytes; bufattr.minreq = (int32_t)-1; bufattr.prebuf = (int32_t)-1; bufattr.fragsize = (int32_t)-1; m_pulseHandle = pa_simple_new (server, "SonivoxEAS", PA_STREAM_PLAYBACK, device, "Synthesizer output", &samplespec, nullptr, /* pa_channel_map */ &bufattr, &err); if (!m_pulseHandle) { m_diagnostics << "Failed to create PulseAudio connection"; m_status = false; } //qDebug() << Q_FUNC_INFO << "period_bytes=" << period_bytes; } void SynthRenderer::uninitEAS() { EAS_RESULT eas_res; if (m_easData != nullptr && m_streamHandle != nullptr) { eas_res = EAS_CloseMIDIStream(m_easData, m_streamHandle); if (eas_res != EAS_SUCCESS) { m_diagnostics << QString("EAS_CloseMIDIStream error: %1").arg( eas_res ); } eas_res = EAS_Shutdown(m_easData); if (eas_res != EAS_SUCCESS) { m_diagnostics << QString("EAS_Shutdown error: %1").arg( eas_res ); } m_streamHandle = nullptr; m_easData = nullptr; } //qDebug() << Q_FUNC_INFO; } void SynthRenderer::uninitPulse() { if (m_pulseHandle != nullptr) { pa_simple_free(m_pulseHandle); m_pulseHandle = nullptr; } //qDebug() << Q_FUNC_INFO; } SynthRenderer::~SynthRenderer() { //qDebug() << Q_FUNC_INFO; } void SynthRenderer::initialize(QSettings *settings) { //qDebug() << Q_FUNC_INFO; settings->beginGroup(QSTR_PREFERENCES); m_bufferTime = settings->value(QSTR_BUFFERTIME, DEF_BUFFERTIME).toInt(); int reverbType = settings->value(QSTR_REVERBTYPE, DEF_REVERBTYPE).toInt(); int reverbAmt = settings->value(QSTR_REVERBAMT, DEF_REVERBAMT).toInt(); int chorusType = settings->value(QSTR_CHORUSTYPE, DEF_CHORUSTYPE).toInt(); int chorusAmt = settings->value(QSTR_CHORUSAMT, DEF_CHORUSAMT).toInt(); m_soundfont = settings->value(QSTR_SOUNDFONT, QString()).toString(); settings->endGroup(); initEAS(); initSoundfont(); initReverb(reverbType); setReverbWet(reverbAmt); initChorus(chorusType); setChorusLevel(chorusAmt); } bool SynthRenderer::stopped() { QReadLocker locker(&m_mutex); return m_Stopped; } void SynthRenderer::stop() { QWriteLocker locker(&m_mutex); //qDebug() << Q_FUNC_INFO; uninitEAS(); m_Stopped = true; } void SynthRenderer::run() { int pa_err; unsigned char data[1024]; //qDebug() << Q_FUNC_INFO << "started"; try { initPulse(); //qDebug() << Q_FUNC_INFO << "m_status:" << m_status; m_Stopped = false; if (m_rendering != nullptr) { m_rendering->wakeAll(); } while (!stopped() && m_status) { EAS_RESULT eas_res; EAS_I32 numGen = 0; size_t bytes = 0; QCoreApplication::sendPostedEvents(); if (m_easData != nullptr) { EAS_PCM *buffer = (EAS_PCM *) data; eas_res = EAS_Render(m_easData, buffer, m_bufferSize, &numGen); if (eas_res != EAS_SUCCESS) { m_diagnostics << QString("EAS_Render error: %1").arg(eas_res); } bytes += (size_t) numGen * sizeof(EAS_PCM) * m_channels; // hand over to pulseaudio the rendered buffer if (pa_simple_write (m_pulseHandle, data, bytes, &pa_err) < 0) { m_diagnostics << QString("Error writing to PulseAudio connection: %1").arg(pa_err); } } } uninitPulse(); } catch (...) { m_diagnostics << "Exception in rendering loop - exiting"; m_status = false; } //qDebug() << Q_FUNC_INFO << "ended"; Q_EMIT finished(); } void SynthRenderer::writeMIDIData(const QByteArray& message) { EAS_RESULT eas_res = EAS_ERROR_ALREADY_STOPPED; if (m_easData != nullptr && m_streamHandle != nullptr) { if (message.length() > 0) { //qDebug() << Q_FUNC_INFO << message.toHex(); eas_res = EAS_WriteMIDIStream(m_easData, m_streamHandle, (EAS_U8 *)message.data(), message.length()); if (eas_res != EAS_SUCCESS) { m_diagnostics << QString("EAS_WriteMIDIStream error: %1").arg(eas_res); } } } } void SynthRenderer::initSoundfont() { //qDebug() << Q_FUNC_INFO; if (!m_soundfont.isEmpty()) { for(int ch = 0; ch < MIDI_STD_CHANNELS; ++ch) { if (ch == MIDI_GM_STD_DRUM_CHANNEL) { sendMessage(MIDI_STATUS_CONTROLCHANGE + ch, MIDI_CONTROL_MSB_BANK_SELECT, 0); sendMessage(MIDI_STATUS_CONTROLCHANGE + ch, MIDI_CONTROL_LSB_BANK_SELECT, 127); } else { sendMessage(MIDI_STATUS_CONTROLCHANGE + ch, MIDI_CONTROL_MSB_BANK_SELECT, 0); sendMessage(MIDI_STATUS_CONTROLCHANGE + ch, MIDI_CONTROL_LSB_BANK_SELECT, 0); } sendMessage(MIDI_STATUS_PROGRAMCHANGE + ch, 0); } } } bool SynthRenderer::getStatus() const { return m_status; } QStringList SynthRenderer::getDiagnostics() const { return m_diagnostics; } void SynthRenderer::setCondition(QWaitCondition *cond) { m_rendering = cond; } QString SynthRenderer::getLibVersion() { quint8 v1, v2, v3, v4; v1 = (m_libVersion >> 24) & 0xff; v2 = (m_libVersion >> 16) & 0xff; v3 = (m_libVersion >> 8) & 0xff; v4 = m_libVersion & 0xff; QVersionNumber vn{v1, v2, v3, v4}; return vn.toString(); } QString SynthRenderer::getSoundFont() { return m_soundfont; } void SynthRenderer::writeSettings(QSettings *settings) { if (settings != nullptr) { settings->beginGroup(QSTR_PREFERENCES); settings->setValue(QSTR_BUFFERTIME, m_bufferTime); settings->setValue(QSTR_REVERBTYPE, m_reverbType); settings->setValue(QSTR_REVERBAMT, m_reverbAmt); settings->setValue(QSTR_CHORUSTYPE, m_chorusType); settings->setValue(QSTR_CHORUSAMT, m_chorusAmt); settings->setValue(QSTR_SOUNDFONT, m_soundfont); settings->endGroup(); } } void SynthRenderer::initReverb(int reverb_type) { EAS_RESULT eas_res; EAS_BOOL sw = EAS_TRUE; if ( reverb_type >= EAS_PARAM_REVERB_LARGE_HALL && reverb_type <= EAS_PARAM_REVERB_ROOM ) { sw = EAS_FALSE; eas_res = EAS_SetParameter(m_easData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_PRESET, (EAS_I32) reverb_type); if (eas_res != EAS_SUCCESS) { m_diagnostics << QString("EAS_SetParameter error: %1").arg(eas_res); } else { m_reverbType = reverb_type; } } eas_res = EAS_SetParameter(m_easData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_BYPASS, sw); if (eas_res != EAS_SUCCESS) { m_diagnostics << QString("EAS_SetParameter error: %1").arg(eas_res); } } void SynthRenderer::initChorus(int chorus_type) { EAS_RESULT eas_res; EAS_BOOL sw = EAS_TRUE; if (chorus_type >= EAS_PARAM_CHORUS_PRESET1 && chorus_type <= EAS_PARAM_CHORUS_PRESET4 ) { sw = EAS_FALSE; eas_res = EAS_SetParameter(m_easData, EAS_MODULE_CHORUS, EAS_PARAM_CHORUS_PRESET, (EAS_I32) chorus_type); if (eas_res != EAS_SUCCESS) { m_diagnostics << QString("EAS_SetParameter error: %1").arg(eas_res); } else { m_chorusType = chorus_type; } } eas_res = EAS_SetParameter(m_easData, EAS_MODULE_CHORUS, EAS_PARAM_CHORUS_BYPASS, sw); if (eas_res != EAS_SUCCESS) { m_diagnostics << QString("EAS_SetParameter error: %1").arg(eas_res); } } void SynthRenderer::setReverbWet(int amount) { EAS_RESULT eas_res = EAS_SetParameter(m_easData, EAS_MODULE_REVERB, EAS_PARAM_REVERB_WET, (EAS_I32) amount); if (eas_res != EAS_SUCCESS) { m_diagnostics << QString("EAS_SetParameter error: %1").arg(eas_res); } else { m_reverbAmt = amount; } } void SynthRenderer::setChorusLevel(int amount) { EAS_RESULT eas_res = EAS_SetParameter(m_easData, EAS_MODULE_CHORUS, EAS_PARAM_CHORUS_LEVEL, (EAS_I32) amount); if (eas_res != EAS_SUCCESS) { m_diagnostics << QString("EAS_SetParameter error: %1").arg(eas_res); } else { m_chorusAmt = amount; } } void SynthRenderer::sendMessage(int m0) { QByteArray m; m.resize(1); m[0] = m0; writeMIDIData(m); } void SynthRenderer::sendMessage(int m0, int m1) { QByteArray m; m.resize(2); m[0] = m0; m[1] = m1; writeMIDIData(m); } void SynthRenderer::sendMessage(int m0, int m1, int m2) { QByteArray m; m.resize(3); m[0] = m0; m[1] = m1; m[2] = m2; writeMIDIData(m); } MIDIConnection SynthRenderer::connection() { if (stopped()) { return MIDIConnection(); } else { return MIDIConnection(QSTR_SONIVOXEAS, QSTR_SONIVOXEAS); } } void SynthRenderer::setBufferTime(int milliseconds) { //qDebug() << Q_FUNC_INFO << milliseconds; m_bufferTime = milliseconds; } } // namespace rt } // namespace drumstick drumstick-2.9.0/library/rt-backends/eassynth/synthrenderer.h0000644000175000017500000000665214541630232023310 0ustar pedropedro/* Sonivox EAS Synthesizer for Qt applications Copyright (C) 2016-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SYNTHRENDERER_H_ #define SYNTHRENDERER_H_ #include #include #include #include #include #include #include "eas.h" namespace drumstick { namespace rt { class SynthRenderer : public QObject { Q_OBJECT public: explicit SynthRenderer(QObject *parent = nullptr); virtual ~SynthRenderer(); void stop(); bool stopped(); void initReverb(int reverb_type); void initChorus(int chorus_type); void setReverbWet(int amount); void setChorusLevel(int amount); void sendMessage(int m0); void sendMessage(int m0, int m1); void sendMessage(int m0, int m1, int m2); MIDIConnection connection(); void setBufferTime(int milliseconds); void initialize(QSettings* settings); bool getStatus() const; QStringList getDiagnostics() const; void setCondition(QWaitCondition *cond); QString getLibVersion(); QString getSoundFont(); void writeSettings(QSettings *settings); static const QString QSTR_PREFERENCES; static const QString QSTR_BUFFERTIME; static const QString QSTR_REVERBTYPE; static const QString QSTR_REVERBAMT; static const QString QSTR_CHORUSTYPE; static const QString QSTR_CHORUSAMT; static const QString QSTR_SONIVOXEAS; static const QString QSTR_SOUNDFONT; static const int DEF_BUFFERTIME; static const int DEF_REVERBTYPE; static const int DEF_REVERBAMT; static const int DEF_CHORUSTYPE; static const int DEF_CHORUSAMT; private: void initEAS(); void initPulse(); void uninitEAS(); void uninitPulse(); void writeMIDIData(const QByteArray& message); void initSoundfont(); public Q_SLOTS: void run(); Q_SIGNALS: void finished(); private: bool m_Stopped; QReadWriteLock m_mutex; QWaitCondition *m_rendering; /* SONiVOX EAS */ int m_sampleRate, m_bufferSize, m_channels; EAS_DATA_HANDLE m_easData; EAS_HANDLE m_streamHandle; QString m_soundfont; /* pulseaudio */ int m_bufferTime{DEF_BUFFERTIME}; pa_simple *m_pulseHandle; /* object properties */ bool m_status; QStringList m_diagnostics; EAS_U32 m_libVersion; int m_reverbType{DEF_REVERBTYPE}; int m_reverbAmt{DEF_REVERBAMT}; int m_chorusType{DEF_CHORUSTYPE}; int m_chorusAmt{DEF_CHORUSAMT}; }; }} /* drumstick::rt */ #endif /*SYNTHRENDERER_H_*/ drumstick-2.9.0/library/rt-backends/fluidsynth/0000755000175000017500000000000014541630232020565 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/fluidsynth/fluidsynth.pro0000644000175000017500000000130614541630232023500 0ustar pedropedroTEMPLATE = lib TARGET = $$qtLibraryTarget(drumstick-rt-fluidsynth) DESTDIR = ../../../build/lib/drumstick2 include (../../../global.pri) CONFIG += c++11 plugin #create_prl DEPENDPATH += . ../../include INCLUDEPATH += . ../../include QT -= gui HEADERS += fluidsynthengine.h \ fluidsynthoutput.h SOURCES += fluidsynthoutput.cpp fluidsynthengine.cpp LIBS += -L$$OUT_PWD/../../../build/lib -ldrumstick-rt macx { INCLUDEPATH += /Library/Frameworks/FluidSynth.framework/Headers QMAKE_LFLAGS += -F/Library/Frameworks LIBS += -framework FluidSynth } else { CONFIG += link_pkgconfig packagesExist(fluidsynth) { PKGCONFIG += fluidsynth } } win32 { TARGET_EXT = .dll } drumstick-2.9.0/library/rt-backends/fluidsynth/CMakeLists.txt0000644000175000017500000000512714541630232023332 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-rt-fluidsynth_QTOBJ_SRCS fluidsynthengine.h fluidsynthoutput.h ) set(drumstick-rt-fluidsynth_SRCS fluidsynthengine.cpp fluidsynthoutput.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt-fluidsynth_MOC_SRCS ${drumstick-rt-fluidsynth_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp(drumstick-rt-fluidsynth_MOC_SRCS ${drumstick-rt-fluidsynth_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library(drumstick-rt-fluidsynth STATIC ${drumstick-rt-fluidsynth_MOC_SRCS} ${drumstick-rt-fluidsynth_SRCS}) target_compile_definitions(drumstick-rt-fluidsynth PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-fluidsynth PROPERTIES STATIC_LIB "libdrumstick-rt-fluidsynth") else() add_library(drumstick-rt-fluidsynth MODULE ${drumstick-rt-fluidsynth_MOC_SRCS} ${drumstick-rt-fluidsynth_SRCS}) target_compile_definitions(drumstick-rt-fluidsynth PRIVATE QT_PLUGIN) endif() target_include_directories(drumstick-rt-fluidsynth PRIVATE ${Drumstick_SOURCE_DIR}/library/include ) target_link_libraries(drumstick-rt-fluidsynth PRIVATE Qt${QT_VERSION_MAJOR}::Core Drumstick::RT PkgConfig::FLUIDSYNTH ) set_target_properties(drumstick-rt-fluidsynth PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install(TARGETS drumstick-rt-fluidsynth EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR}) drumstick-2.9.0/library/rt-backends/fluidsynth/fluidsynthengine.cpp0000644000175000017500000003207614541630232024660 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include #include #include #include #include "fluidsynthengine.h" namespace drumstick { namespace rt { const QString FluidSynthEngine::QSTR_FLUIDSYNTH_VERSION = QStringLiteral(FLUIDSYNTH_VERSION); const QString FluidSynthEngine::QSTR_FLUIDSYNTH = QStringLiteral("FluidSynth"); const QString FluidSynthEngine::QSTR_PREFERENCES = QStringLiteral("FluidSynth"); const QString FluidSynthEngine::QSTR_INSTRUMENTSDEFINITION = QStringLiteral("InstrumentsDefinition"); const QString FluidSynthEngine::QSTR_DATADIR = QStringLiteral("soundfonts"); const QString FluidSynthEngine::QSTR_DATADIR2 = QStringLiteral("sounds/sf2"); const QString FluidSynthEngine::QSTR_SOUNDFONT = QStringLiteral("default.sf2"); const QString FluidSynthEngine::QSTR_PULSEAUDIO = QStringLiteral("pulseaudio"); const QString FluidSynthEngine::QSTR_AUDIODRIVER = QStringLiteral("AudioDriver"); const QString FluidSynthEngine::QSTR_BUFFERTIME = QStringLiteral("BufferTime"); const QString FluidSynthEngine::QSTR_PERIODSIZE = QStringLiteral("PeriodSize"); const QString FluidSynthEngine::QSTR_PERIODS = QStringLiteral("Periods"); const QString FluidSynthEngine::QSTR_SAMPLERATE = QStringLiteral("SampleRate"); const QString FluidSynthEngine::QSTR_CHORUS = QStringLiteral("Chorus"); const QString FluidSynthEngine::QSTR_REVERB = QStringLiteral("Reverb"); const QString FluidSynthEngine::QSTR_GAIN = QStringLiteral("Gain"); const QString FluidSynthEngine::QSTR_POLYPHONY = QStringLiteral("Polyphony"); const QString FluidSynthEngine::QSTR_DEFAULT_AUDIODRIVER = #if defined(Q_OS_LINUX) QSTR_PULSEAUDIO; #elif defined(Q_OS_WIN) QStringLiteral("wasapi"); #elif defined(Q_OS_OSX) QStringLiteral("coreaudio"); #else QStringLiteral("oss"); #endif const int FluidSynthEngine::DEFAULT_PERIODS = 8; const int FluidSynthEngine::DEFAULT_PERIODSIZE = 512; const double FluidSynthEngine::DEFAULT_SAMPLERATE = 44100.0; const int FluidSynthEngine::DEFAULT_CHORUS = 0; const int FluidSynthEngine::DEFAULT_REVERB = 1; const double FluidSynthEngine::DEFAULT_GAIN = 1.0; const int FluidSynthEngine::DEFAULT_POLYPHONY = 256; static void SynthEngine_log_function(int level, const char* message, void* data) { FluidSynthEngine *classInstance = static_cast(data); classInstance->appendDiagnostics(level, message); } FluidSynthEngine::FluidSynthEngine(QObject *parent) : QObject(parent), m_sfid(0), m_settings(nullptr), m_synth(nullptr), m_driver(nullptr) { //qDebug() << Q_FUNC_INFO; m_runtimeLibraryVersion = ::fluid_version_str(); //qDebug() << "Compiled FluidSynth Version:" << QSTR_FLUIDSYNTH_VERSION; //qDebug() << "Runtime FluidSynth Version:" << m_runtimeLibraryVersion; //::fluid_set_log_function(fluid_log_level::FLUID_DBG, &SynthEngine_log_function, this); ::fluid_set_log_function(fluid_log_level::FLUID_ERR, &SynthEngine_log_function, this); ::fluid_set_log_function(fluid_log_level::FLUID_WARN, &SynthEngine_log_function, this); ::fluid_set_log_function(fluid_log_level::FLUID_INFO, &SynthEngine_log_function, this); } FluidSynthEngine::~FluidSynthEngine() { //qDebug() << Q_FUNC_INFO; uninitialize(); } void FluidSynthEngine::uninitialize() { //qDebug() << Q_FUNC_INFO; if (m_driver != nullptr) { ::delete_fluid_audio_driver(m_driver); m_driver = nullptr; } if (m_synth != nullptr) { ::delete_fluid_synth(m_synth); m_synth = nullptr; } if (m_settings != nullptr) { ::delete_fluid_settings(m_settings); m_settings = nullptr; } m_status = false; m_diagnostics.clear(); } void FluidSynthEngine::initializeSynth() { uninitialize(); //qDebug() << Q_FUNC_INFO << fs_audiodriver << fs_periodSize << fs_periods << qEnvironmentVariableIntValue("PULSE_LATENCY_MSEC"); m_settings = ::new_fluid_settings(); ::fluid_settings_setstr(m_settings, "audio.driver", qPrintable(fs_audiodriver)); ::fluid_settings_setint(m_settings, "audio.period-size", fs_periodSize); ::fluid_settings_setint(m_settings, "audio.periods", fs_periods); if (fs_audiodriver == QSTR_PULSEAUDIO) { ::fluid_settings_setint(m_settings, "audio.pulseaudio.adjust-latency", 0); } ::fluid_settings_setnum(m_settings, "synth.sample-rate", fs_sampleRate); ::fluid_settings_setint(m_settings, "synth.chorus.active", fs_chorus); ::fluid_settings_setint(m_settings, "synth.reverb.active", fs_reverb); ::fluid_settings_setnum(m_settings, "synth.gain", fs_gain); ::fluid_settings_setint(m_settings, "synth.polyphony", fs_polyphony); m_synth = ::new_fluid_synth(m_settings); m_driver = ::new_fluid_audio_driver(m_settings, m_synth); } void FluidSynthEngine::setInstrument(int channel, int pgm) { ::fluid_synth_program_change(m_synth, channel, pgm); } void FluidSynthEngine::noteOn(int channel, int midiNote, int velocity) { ::fluid_synth_noteon(m_synth, channel, midiNote, velocity); } void FluidSynthEngine::noteOff(int channel, int midiNote, int /*velocity*/) { ::fluid_synth_noteoff(m_synth, channel, midiNote); } void FluidSynthEngine::loadSoundFont() { if (m_sfid != -1) { ::fluid_synth_sfunload(m_synth, unsigned(m_sfid), 1); } m_sfid = ::fluid_synth_sfload(m_synth, qPrintable(m_soundFont), 1); } void FluidSynthEngine::retrieveDefaultSoundfont() { /* find the default configured soundfont */ if (m_defSoundFont.isEmpty()) { char *psValue = nullptr; int ok = ::fluid_settings_dupstr(m_settings, "synth.default-soundfont", &psValue); if (ok == FLUID_OK) { m_defSoundFont = QString(psValue); ::fluid_free(psValue); } } } void FluidSynthEngine::initialize() { //qDebug() << Q_FUNC_INFO; initializeSynth(); retrieveAudioDrivers(); retrieveDefaultSoundfont(); if (m_defSoundFont.isEmpty()) { scanSoundFonts(); } if (m_soundFont.isEmpty() && !m_defSoundFont.isEmpty()) { m_soundFont = m_defSoundFont; } loadSoundFont(); m_status = (m_synth != nullptr) && (m_driver != nullptr) && (m_sfid >= 0); } void FluidSynthEngine::panic() { ::fluid_synth_system_reset(m_synth); } void FluidSynthEngine::controlChange(const int channel, const int midiCtl, const int value) { ::fluid_synth_cc(m_synth, channel, midiCtl, value); } void FluidSynthEngine::bender(const int channel, const int value) { ::fluid_synth_pitch_bend(m_synth, channel, value + 8192); } void FluidSynthEngine::channelPressure(const int channel, const int value) { ::fluid_synth_channel_pressure(m_synth, channel, value); } void FluidSynthEngine::keyPressure(const int channel, const int midiNote, const int value) { static const QVersionNumber versionCheck(2,0,0); QVersionNumber fluidVersion = QVersionNumber::fromString(getLibVersion()); if (fluidVersion >= versionCheck) { ::fluid_synth_key_pressure(m_synth, channel, midiNote, value); } } void FluidSynthEngine::sysex(const QByteArray &data) { const unsigned char SYSEX = 0xf0; const unsigned char EOX = 0xf7; QByteArray d(data); if (d.startsWith(SYSEX)) { d.remove(0, 1); } if (d.endsWith(EOX)) { d.chop(1); } ::fluid_synth_sysex(m_synth, d.data(), d.length(), nullptr, nullptr, nullptr, 0); } void FluidSynthEngine::setSoundFont(const QString &value) { if (value != m_soundFont) { m_soundFont = value; loadSoundFont(); } } void FluidSynthEngine::appendDiagnostics(int level, const char *message) { static const QMap prefix { {fluid_log_level::FLUID_DBG, tr("Debug")}, {fluid_log_level::FLUID_ERR, tr("Error")}, {fluid_log_level::FLUID_WARN, tr("Warning")}, {fluid_log_level::FLUID_INFO, tr("Information")} }; m_diagnostics.append(prefix[level]+": "+message); } void FluidSynthEngine::stop() { //qDebug() << Q_FUNC_INFO; uninitialize(); } QStringList FluidSynthEngine::getAudioDrivers() { return m_audioDriversList; } QStringList FluidSynthEngine::getDiagnostics() { return m_diagnostics; } QString FluidSynthEngine::getLibVersion() { return m_runtimeLibraryVersion; } bool FluidSynthEngine::getStatus() { return m_status; } void FluidSynthEngine::writeSettings(QSettings *settings) { if (settings != nullptr) { settings->beginGroup(QSTR_PREFERENCES); settings->setValue(QSTR_INSTRUMENTSDEFINITION, m_soundFont); settings->setValue(QSTR_AUDIODRIVER, fs_audiodriver); settings->setValue(QSTR_PERIODSIZE, fs_periodSize); settings->setValue(QSTR_PERIODS, fs_periods); settings->setValue(QSTR_SAMPLERATE, fs_sampleRate); settings->setValue(QSTR_CHORUS, fs_chorus); settings->setValue(QSTR_REVERB, fs_reverb); settings->setValue(QSTR_GAIN, fs_gain); settings->setValue(QSTR_POLYPHONY, fs_polyphony); int bufferTime = 1000 * fs_periodSize * fs_periods / fs_sampleRate; settings->setValue(QSTR_BUFFERTIME, bufferTime); settings->endGroup(); } } void FluidSynthEngine::scanSoundFonts(const QDir &initialDir) { QDir dir(initialDir); dir.setFilter(QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot); dir.setSorting(QDir::Name); QStringList filters; filters << "*.sf2" << "*.SF2" << "*.sf3" << "*.SF3" << "*.dls" << "*.DLS"; QFileInfoList entries= dir.entryInfoList(filters); foreach(const QFileInfo &info, entries) { QString name = info.absoluteFilePath(); if (info.isFile() && info.fileName().toLower() == QSTR_SOUNDFONT) { m_soundFontsList << name; } else if (info.isDir()){ scanSoundFonts(name); } } } void FluidSynthEngine::retrieveAudioDrivers() { if (m_settings != nullptr) { m_audioDriversList.clear(); ::fluid_settings_foreach_option(m_settings, "audio.driver", &m_audioDriversList, [](void *context2, const char *, const char *option2){ QStringList *options_list = static_cast(context2); options_list->append(option2); }); //qDebug() << "Drivers: " << m_audioDriversList; } } void FluidSynthEngine::scanSoundFonts() { if (m_defSoundFont.isEmpty()) { m_soundFontsList.clear(); QStringList paths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); #if defined(Q_OS_OSX) paths << (QCoreApplication::applicationDirPath() + QLatin1String("../Resources")); #endif foreach(const QString& p, paths) { QDir d(p + QDir::separator() + QSTR_DATADIR); if (!d.exists()) { d = QDir(p + QDir::separator() + QSTR_DATADIR2); } if (d.exists()) { scanSoundFonts(d); } } if (m_soundFontsList.length() > 0) { m_defSoundFont = m_soundFontsList.first(); } } } void FluidSynthEngine::readSettings(QSettings *settings) { m_sfid = -1; settings->beginGroup(QSTR_PREFERENCES); m_soundFont = settings->value(QSTR_INSTRUMENTSDEFINITION, m_defSoundFont).toString(); fs_audiodriver = settings->value(QSTR_AUDIODRIVER, QSTR_DEFAULT_AUDIODRIVER).toString(); fs_periodSize = settings->value(QSTR_PERIODSIZE, DEFAULT_PERIODSIZE).toInt(); fs_periods = settings->value(QSTR_PERIODS, DEFAULT_PERIODS).toInt(); fs_sampleRate = settings->value(QSTR_SAMPLERATE, DEFAULT_SAMPLERATE).toDouble(); fs_chorus = settings->value(QSTR_CHORUS, DEFAULT_CHORUS).toInt(); fs_reverb = settings->value(QSTR_REVERB, DEFAULT_REVERB).toInt(); fs_gain = settings->value(QSTR_GAIN, DEFAULT_GAIN).toDouble(); fs_polyphony = settings->value(QSTR_POLYPHONY, DEFAULT_POLYPHONY).toInt(); settings->endGroup(); //qDebug() << Q_FUNC_INFO << "audioDriver:" << fs_audiodriver << "buffer" << fs_periodSize << '*' << fs_periods; if (fs_audiodriver == QSTR_PULSEAUDIO) { int bufferTime = 1000 * fs_periodSize * fs_periods / fs_sampleRate; qputenv("PULSE_LATENCY_MSEC", QByteArray::number( bufferTime ) ); //qDebug() << Q_FUNC_INFO << "$PULSE_LATENCY_MSEC=" << bufferTime; } } void FluidSynthEngine::close() { //qDebug() << Q_FUNC_INFO; m_currentConnection = MIDIConnection(); } void FluidSynthEngine:: open() { //qDebug() << Q_FUNC_INFO; m_currentConnection = MIDIConnection(QSTR_FLUIDSYNTH, QSTR_FLUIDSYNTH); } } // namespace rt } // namespace drumstick drumstick-2.9.0/library/rt-backends/fluidsynth/fluidsynthengine.h0000644000175000017500000001073114541630232024317 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #ifndef SynthEngine_H #define SynthEngine_H #include #include #include #include #include #include #include #include namespace drumstick { namespace rt { class FluidSynthEngine : public QObject { Q_OBJECT Q_PROPERTY(QString soundFont READ soundFont WRITE setSoundFont) public: explicit FluidSynthEngine(QObject *parent = nullptr); virtual ~FluidSynthEngine(); QString soundFont() const { return m_soundFont; } void setSoundFont(const QString &value); void appendDiagnostics(int level, const char* message); void stop(); Q_INVOKABLE void initialize(); Q_INVOKABLE void readSettings(QSettings *settings); Q_INVOKABLE void scanSoundFonts(); Q_INVOKABLE void panic(); Q_INVOKABLE void setInstrument(const int channel, int i); Q_INVOKABLE void noteOn(const int channel, const int midiNote, const int velocity); Q_INVOKABLE void noteOff(const int channel, const int midiNote, const int velocity); Q_INVOKABLE void controlChange(const int channel, const int ctl, const int value); Q_INVOKABLE void bender(const int channel, const int value); Q_INVOKABLE void channelPressure(const int channel, const int value); Q_INVOKABLE void keyPressure(const int channel, const int midiNote, const int value); Q_INVOKABLE void sysex(const QByteArray& data); Q_INVOKABLE QString version() const { return QT_STRINGIFY(VERSION); } MIDIConnection currentConnection() const { return m_currentConnection; } void close(); void open(); void uninitialize(); QStringList getAudioDrivers(); QStringList getDiagnostics(); QString getLibVersion(); bool getStatus(); Q_INVOKABLE void writeSettings(QSettings *settings); static const QString QSTR_FLUIDSYNTH_VERSION; static const QString QSTR_FLUIDSYNTH; static const QString QSTR_PREFERENCES; static const QString QSTR_INSTRUMENTSDEFINITION; static const QString QSTR_DATADIR; static const QString QSTR_DATADIR2; static const QString QSTR_SOUNDFONT; static const QString QSTR_AUDIODRIVER; static const QString QSTR_PERIODSIZE; static const QString QSTR_PERIODS; static const QString QSTR_SAMPLERATE; static const QString QSTR_CHORUS; static const QString QSTR_REVERB; static const QString QSTR_GAIN; static const QString QSTR_POLYPHONY; static const QString QSTR_DEFAULT_AUDIODRIVER; static const QString QSTR_BUFFERTIME; static const QString QSTR_PULSEAUDIO; static const int DEFAULT_PERIODS; static const int DEFAULT_PERIODSIZE; static const double DEFAULT_SAMPLERATE; static const int DEFAULT_CHORUS; static const int DEFAULT_REVERB; static const double DEFAULT_GAIN; static const int DEFAULT_POLYPHONY; private: void scanSoundFonts(const QDir &dir); void retrieveAudioDrivers(); void initializeSynth(); void loadSoundFont(); void retrieveDefaultSoundfont(); int m_sfid; MIDIConnection m_currentConnection; QString m_runtimeLibraryVersion; QString m_soundFont; QString m_defSoundFont; fluid_settings_t* m_settings; fluid_synth_t* m_synth; fluid_audio_driver_t* m_driver; QStringList m_soundFontsList; QStringList m_audioDriversList; QString fs_audiodriver{ QSTR_DEFAULT_AUDIODRIVER }; int fs_periodSize { DEFAULT_PERIODSIZE }; int fs_periods { DEFAULT_PERIODS }; double fs_sampleRate { DEFAULT_SAMPLERATE }; int fs_chorus { DEFAULT_CHORUS }; int fs_reverb { DEFAULT_REVERB }; double fs_gain { DEFAULT_GAIN }; int fs_polyphony { DEFAULT_POLYPHONY }; bool m_status; QStringList m_diagnostics; }; }} // namespace drumstick::rt #endif // SynthEngine_H drumstick-2.9.0/library/rt-backends/fluidsynth/fluidsynthoutput.cpp0000644000175000017500000000736014541630232024751 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "fluidsynthoutput.h" namespace drumstick { namespace rt { FluidSynthOutput::FluidSynthOutput(QObject *parent) : MIDIOutput(parent) { //qDebug() << Q_FUNC_INFO; m_synth = new FluidSynthEngine; } FluidSynthOutput::~FluidSynthOutput() { //qDebug() << Q_FUNC_INFO; stop(); delete m_synth; } void FluidSynthOutput::start() { //qDebug() << Q_FUNC_INFO; m_synth->initialize(); } void FluidSynthOutput::stop() { //qDebug() << Q_FUNC_INFO; m_synth->stop(); } QStringList FluidSynthOutput::getAudioDrivers() { return m_synth->getAudioDrivers(); } QStringList FluidSynthOutput::getDiagnostics() { return m_synth->getDiagnostics(); } QString FluidSynthOutput::getLibVersion() { return m_synth->getLibVersion(); } bool FluidSynthOutput::getStatus() { return m_synth->getStatus(); } QString FluidSynthOutput::getSoundFont() { return m_synth->soundFont(); } void FluidSynthOutput::initialize(QSettings *settings) { //qDebug() << Q_FUNC_INFO; m_synth->readSettings(settings); stop(); start(); } QString FluidSynthOutput::backendName() { return FluidSynthEngine::QSTR_FLUIDSYNTH; } QString FluidSynthOutput::publicName() { return FluidSynthEngine::QSTR_FLUIDSYNTH; } void FluidSynthOutput::setPublicName(QString name) { Q_UNUSED(name) } QList FluidSynthOutput::connections(bool advanced) { Q_UNUSED(advanced) return QList{MIDIConnection(FluidSynthEngine::QSTR_FLUIDSYNTH, FluidSynthEngine::QSTR_FLUIDSYNTH)}; } void FluidSynthOutput::setExcludedConnections(QStringList conns) { Q_UNUSED(conns) } void FluidSynthOutput::open(const MIDIConnection& name) { Q_UNUSED(name) //qDebug() << Q_FUNC_INFO; m_synth->open(); } void FluidSynthOutput::close() { //qDebug() << Q_FUNC_INFO; m_synth->close(); stop(); } MIDIConnection FluidSynthOutput::currentConnection() { return m_synth->currentConnection(); } void FluidSynthOutput::sendNoteOff(int chan, int note, int vel) { m_synth->noteOff(chan, note, vel); } void FluidSynthOutput::sendNoteOn(int chan, int note, int vel) { m_synth->noteOn(chan, note, vel); } void FluidSynthOutput::sendKeyPressure(int chan, int note, int value) { m_synth->keyPressure(chan, note, value); } void FluidSynthOutput::sendController(int chan, int control, int value) { m_synth->controlChange(chan, control, value); } void FluidSynthOutput::sendProgram(int chan, int program) { m_synth->setInstrument(chan, program); } void FluidSynthOutput::sendChannelPressure(int chan, int value) { m_synth->channelPressure(chan, value); } void FluidSynthOutput::sendPitchBend(int chan, int value) { m_synth->bender(chan, value); } void FluidSynthOutput::sendSysex(const QByteArray &data) { m_synth->sysex(data); } void FluidSynthOutput::sendSystemMsg(const int status) { Q_UNUSED(status) } void FluidSynthOutput::writeSettings(QSettings *settings) { m_synth->writeSettings(settings); } } // namespace rt } // namespace drumstick drumstick-2.9.0/library/rt-backends/fluidsynth/fluidsynthoutput.h0000644000175000017500000000620114541630232024407 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SynthOUTPUT_H #define SynthOUTPUT_H #include #include #include #include #include "fluidsynthengine.h" namespace drumstick { namespace rt { class FluidSynthOutput : public MIDIOutput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIOutput/2.0") Q_INTERFACES(drumstick::rt::MIDIOutput) Q_PROPERTY(QStringList audiodrivers READ getAudioDrivers) Q_PROPERTY(QStringList diagnostics READ getDiagnostics) Q_PROPERTY(QString libversion READ getLibVersion) Q_PROPERTY(bool status READ getStatus) Q_PROPERTY(QString soundfont READ getSoundFont) public: explicit FluidSynthOutput(QObject *parent = nullptr); virtual ~FluidSynthOutput(); void start(); void stop(); // MIDIOutput interface public: virtual void initialize(QSettings* settings) override; virtual QString backendName() override; virtual QString publicName() override; virtual void setPublicName(QString name) override; virtual QList connections(bool advanced) override; virtual void setExcludedConnections(QStringList conns) override; virtual void open(const MIDIConnection& name) override; virtual void close() override; virtual MIDIConnection currentConnection() override; public Q_SLOTS: virtual void sendNoteOff(int chan, int note, int vel) override; virtual void sendNoteOn(int chan, int note, int vel) override; virtual void sendKeyPressure(int chan, int note, int value) override; virtual void sendController(int chan, int control, int value) override; virtual void sendProgram(int chan, int program) override; virtual void sendChannelPressure(int chan, int value) override; virtual void sendPitchBend(int chan, int value) override; virtual void sendSysex(const QByteArray &data) override; virtual void sendSystemMsg(const int status) override; void writeSettings(QSettings *settings); private: QPointer m_synth; private: QStringList getAudioDrivers(); QStringList getDiagnostics(); QString getLibVersion(); bool getStatus(); QString getSoundFont(); }; }} // namespace drumstick::rt #endif // SynthOUTPUT_H drumstick-2.9.0/library/rt-backends/mac-in/0000755000175000017500000000000014541630232017540 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/mac-in/CMakeLists.txt0000644000175000017500000000521014541630232022276 0ustar pedropedro# MIDI Sequencer C++ Library # Copyright (C) 2005-2023 Pedro Lopez-Cabanillas # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-rt-mac-in_QTOBJ_SRCS macmidiinput.h ) set(drumstick-rt-mac-in_SRCS ../common/maccommon.h ../common/maccommon.cpp ../common/maccommon.mm macmidiinput.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt-mac-in_MOC_SRCS ${drumstick-rt-mac-in_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp(drumstick-rt-mac-in_MOC_SRCS ${drumstick-rt-mac-in_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library(drumstick-rt-mac-in STATIC ${drumstick-rt-mac-in_MOC_SRCS} ${drumstick-rt-mac-in_SRCS}) target_compile_definitions(drumstick-rt-mac-in PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-mac-in PROPERTIES STATIC_LIB "libdrumstick-rt-mac-in") else() add_library(drumstick-rt-mac-in MODULE ${drumstick-rt-mac-in_MOC_SRCS} ${drumstick-rt-mac-in_SRCS}) target_compile_definitions(drumstick-rt-mac-in PRIVATE QT_PLUGIN) endif() target_include_directories(drumstick-rt-mac-in PRIVATE ${Drumstick_SOURCE_DIR}/library/include ../common ) target_link_libraries(drumstick-rt-mac-in PUBLIC Drumstick::RT Qt${QT_VERSION_MAJOR}::Core "-framework CoreMIDI -framework CoreFoundation -framework CoreServices -framework Foundation" ) if (QT_VERSION VERSION_LESS 6.0.0) target_link_libraries( drumstick-rt-mac-in PUBLIC Qt${QT_VERSION_MAJOR}::Concurrent ) endif() set_target_properties(drumstick-rt-mac-in PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install(TARGETS drumstick-rt-mac-in EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR}) drumstick-2.9.0/library/rt-backends/mac-in/mac-in.pro0000644000175000017500000000104114541630232021422 0ustar pedropedroTEMPLATE = lib CONFIG += c++11 plugin static { CONFIG += staticlib create_prl } TARGET = drumstick-rt-mac-in DESTDIR = ../../../build/lib/drumstick2 DEPENDPATH += . ../../include ../common INCLUDEPATH += . ../../include ../common include (../../../global.pri) QT -= gui QT += concurrent HEADERS += macmidiinput.h \ ../common/maccommon.h SOURCES += macmidiinput.cpp \ ../common/maccommon.cpp !static:LIBS += -F$$OUT_PWD/../../../build/lib -framework drumstick-rt LIBS += -framework CoreMIDI -framework CoreFoundation drumstick-2.9.0/library/rt-backends/mac-in/macmidiinput.cpp0000644000175000017500000002771014541630232022736 0ustar pedropedro/* Drumstick RT Backend Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "macmidiinput.h" #include "maccommon.h" #include #include #include #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) #include #endif #include #include namespace drumstick { namespace rt { static CFStringRef DEFAULT_PUBLIC_NAME CFSTR("MIDI In"); static void MacMIDIReadProc( const MIDIPacketList *pktlist, void *refCon, void *connRefCon ); class MacMIDIInputPrivate { public: MacMIDIInput *m_inp; MIDIOutput *m_out; MIDIClientRef m_client; MIDIPortRef m_port; MIDIEndpointRef m_endpoint; MIDIEndpointRef m_source; bool m_thruEnabled; bool m_clientFilter; QString m_publicName; MIDIConnection m_currentInput; QStringList m_excludedNames; QList m_inputDevices; bool m_status; QStringList m_diagnostics; explicit MacMIDIInputPrivate(MacMIDIInput *inp) : m_inp(inp), m_out(nullptr), m_client(0), m_port(0), m_endpoint(0), m_source(0), m_thruEnabled(false), m_clientFilter(true), m_publicName(QString::fromCFString(DEFAULT_PUBLIC_NAME)) { internalCreate( DEFAULT_PUBLIC_NAME ); } void registerStatus(const QString& context, const OSStatus status) { if (status != noErr) { m_diagnostics << QString("%1 error: %2").arg(context, status); m_diagnostics << getErrorTextFromOSStatus(status); } } void internalCreate(CFStringRef name) { OSStatus result = noErr; result = MIDIClientCreate( name , nullptr, nullptr, &m_client ); if (result != noErr) { registerStatus("MIDIClientCreate()", result); return; } result = MIDIDestinationCreate ( m_client, name, MacMIDIReadProc, (void*) this, &m_endpoint ); if (result != noErr) { registerStatus("MIDIDestinationCreate()", result); return; } result = MIDIInputPortCreate( m_client, name, MacMIDIReadProc, (void *) this, &m_port ); if (result != noErr) { registerStatus("MIDIInputPortCreate()", result); return; } reloadDeviceList(true); } virtual ~MacMIDIInputPrivate() { internalDispose(); } void internalDispose() { OSStatus result = noErr; if (m_port != 0) { result = MIDIPortDispose(m_port); if (result != noErr) { registerStatus("MIDIPortDispose()", result); m_port = 0; } } if (m_endpoint != 0) { result = MIDIEndpointDispose(m_endpoint); if (result != noErr) { registerStatus("MIDIEndpointDispose()", result); m_endpoint = 0; } } if (m_client != 0) { result = MIDIClientDispose(m_client); if (result != noErr) { registerStatus("MIDIClientDispose()", result); m_client = 0; } } } void reloadDeviceList(bool advanced) { int num = MIDIGetNumberOfSources(); m_clientFilter = !advanced; m_inputDevices.clear(); m_inputDevices << MIDIConnection(); for (int i = 0; i < num; ++i) { bool excluded = false; MIDIEndpointRef dest = MIDIGetSource( i ); if (dest != 0) { QString name = getEndpointName(dest); if ( m_clientFilter && name.contains(QStringLiteral("IAC"), Qt::CaseSensitive) ) continue; if ( name.contains(m_publicName)) continue; for (const QString& n : m_excludedNames) { if (name.contains(n)) { excluded = true; break; } } if (!excluded) { m_inputDevices << MIDIConnection(name, i); } } } if (!m_currentInput.first.isEmpty() && m_source != 0 && !m_inputDevices.contains(m_currentInput)) { m_currentInput = MIDIConnection(); m_source = 0; } } void setPublicName(QString name) { if (m_publicName != name) { internalDispose(); internalCreate(name.toCFString()); m_publicName = name; } } void open(const MIDIConnection& conn) { OSStatus result = noErr; m_diagnostics.clear(); m_status = false; m_source = MIDIGetSource( conn.second.toInt() ); result = MIDIPortConnectSource( m_port, m_source, nullptr ); if (result != noErr) { registerStatus("MIDIPortConnectSource()", result); return; } m_currentInput = conn; m_status = (result == noErr); return; } void close() { OSStatus result = noErr; m_status = false; m_diagnostics.clear(); if (m_source != 0) { result = MIDIPortDisconnectSource(m_port, m_source); if (result != noErr) { registerStatus("MIDIPortDisconnectSource()", result); } m_source = 0; m_currentInput = MIDIConnection(); m_status = (result == noErr); } } void emitSignals(QByteArray& packet) { int value = 0, j = 0; while(j < packet.length()) { int status = packet[j] & 0xf0; int channel = packet[j] & 0x0f; QByteArray data; switch (status) { case MIDI_STATUS_NOTEOFF: if(m_out != nullptr && m_thruEnabled) m_out->sendNoteOff(channel, packet[j+1], packet[j+2]); emit m_inp->midiNoteOff(channel, packet[j+1], packet[j+2]); j+=3; break; case MIDI_STATUS_NOTEON: if(m_out != nullptr && m_thruEnabled) m_out->sendNoteOn(channel, packet[j+1], packet[j+2]); emit m_inp->midiNoteOn(channel, packet[j+1], packet[j+2]); j+=3; break; case MIDI_STATUS_KEYPRESURE: if(m_out != nullptr && m_thruEnabled) m_out->sendKeyPressure(channel, packet[j+1], packet[j+2]); emit m_inp->midiKeyPressure(channel, packet[j+1], packet[j+2]); j+=3; break; case MIDI_STATUS_CONTROLCHANGE: if(m_out != nullptr && m_thruEnabled) m_out->sendController(channel, packet[j+1], packet[j+2]); emit m_inp->midiController(channel, packet[j+1], packet[j+2]); j+=3; break; case MIDI_STATUS_PROGRAMCHANGE: if(m_out != nullptr && m_thruEnabled) m_out->sendProgram(channel, packet[j+1]); emit m_inp->midiProgram(channel, packet[j+1]); j+=2; break; case MIDI_STATUS_CHANNELPRESSURE: if(m_out != nullptr && m_thruEnabled) m_out->sendChannelPressure(channel, packet[j+1]); emit m_inp->midiChannelPressure(channel, packet[j+1]); j+=2; break; case MIDI_STATUS_PITCHBEND: value = (packet[j+1] + packet[j+2] * 0x80) - 8192; if(m_out != nullptr && m_thruEnabled) m_out->sendPitchBend(channel, value); emit m_inp->midiPitchBend(channel, value); j+=3; break; case MIDI_STATUS_SYSEX: if(m_out != nullptr && m_thruEnabled) m_out->sendSysex(packet); emit m_inp->midiSysex(packet); j+=packet.length(); break; default: registerStatus("invalid status", status); } } } }; static void MacMIDIReadProc( const MIDIPacketList *pktlist, void *refCon, void *connRefCon ) { Q_UNUSED(connRefCon) MacMIDIInputPrivate *obj = nullptr; if (refCon != nullptr) obj = static_cast(refCon); const MIDIPacket *packet = static_cast(pktlist->packet); for (unsigned int i = 0; i < pktlist->numPackets; ++i) { if (obj != nullptr && packet != nullptr) { QByteArray data((const char *)packet->data, packet->length); #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) QtConcurrent::run(obj, &MacMIDIInputPrivate::emitSignals, data); #else obj->emitSignals(data); #endif } packet = MIDIPacketNext(packet); } } MacMIDIInput::MacMIDIInput(QObject *parent) : MIDIInput(parent), d(new MacMIDIInputPrivate(this)) { } MacMIDIInput::~MacMIDIInput() { delete d; } void MacMIDIInput::initialize(QSettings *settings) { Q_UNUSED(settings) } QString MacMIDIInput::backendName() { return QLatin1String("CoreMIDI"); } QString MacMIDIInput::publicName() { return d->m_publicName; } void MacMIDIInput::setPublicName(QString name) { d->setPublicName(name); } QList MacMIDIInput::connections(bool advanced) { d->reloadDeviceList(advanced); return d->m_inputDevices; } void MacMIDIInput::setExcludedConnections(QStringList conns) { d->m_excludedNames = conns; } void MacMIDIInput::open(const MIDIConnection& conn) { d->open(conn); } void MacMIDIInput::close() { d->close(); } MIDIConnection MacMIDIInput::currentConnection() { return d->m_currentInput; } void MacMIDIInput::setMIDIThruDevice(MIDIOutput *device) { d->m_out = device; } void MacMIDIInput::enableMIDIThru(bool enable) { d->m_thruEnabled = enable; } bool MacMIDIInput::isEnabledMIDIThru() { return d->m_thruEnabled && d->m_out != 0; } QStringList MacMIDIInput::getDiagnostics() { return d->m_diagnostics; } bool MacMIDIInput::getStatus() { return d->m_status; } }} // namespace drumstick::rt drumstick-2.9.0/library/rt-backends/mac-in/macmidiinput.h0000644000175000017500000000411714541630232022377 0ustar pedropedro/* Drumstick RT Backend Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef MACMIDIOBJECT_H #define MACMIDIOBJECT_H #include namespace drumstick { namespace rt { class MIDIOutput; class MacMIDIInputPrivate; class MacMIDIInput : public MIDIInput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIInput/2.0") Q_INTERFACES(drumstick::rt::MIDIInput) Q_PROPERTY(QStringList diagnostics READ getDiagnostics) Q_PROPERTY(bool status READ getStatus); public: explicit MacMIDIInput(QObject *parent = nullptr); ~MacMIDIInput(); // MIDIInput interface public: virtual void initialize(QSettings* settings); virtual QString backendName(); virtual QString publicName(); virtual void setPublicName(QString name); virtual QList connections(bool advanced); virtual void setExcludedConnections(QStringList conns); virtual void open(const MIDIConnection& name); virtual void close(); virtual MIDIConnection currentConnection(); virtual void setMIDIThruDevice(MIDIOutput *device); virtual void enableMIDIThru(bool enable); virtual bool isEnabledMIDIThru(); private: MacMIDIInputPrivate* const d; QStringList getDiagnostics(); bool getStatus(); }; }} #endif // MACMIDIOBJECT_H drumstick-2.9.0/library/rt-backends/mac-out/0000755000175000017500000000000014541630232017741 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/mac-out/CMakeLists.txt0000644000175000017500000000502214541630232022500 0ustar pedropedro# MIDI Sequencer C++ Library # Copyright (C) 2005-2023 Pedro Lopez-Cabanillas # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-rt-mac-out_QTOBJ_SRCS macmidioutput.h ) set(drumstick-rt-mac-out_SRCS ../common/maccommon.h ../common/maccommon.cpp ../common/maccommon.mm macmidioutput.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt-mac-out_MOC_SRCS ${drumstick-rt-mac-out_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp(drumstick-rt-mac-out_MOC_SRCS ${drumstick-rt-mac-out_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library(drumstick-rt-mac-out STATIC ${drumstick-rt-mac-out_MOC_SRCS} ${drumstick-rt-mac-out_SRCS}) target_compile_definitions(drumstick-rt-mac-out PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-mac-out PROPERTIES STATIC_LIB "libdrumstick-rt-mac-out") else() add_library(drumstick-rt-mac-out MODULE ${drumstick-rt-mac-out_MOC_SRCS} ${drumstick-rt-mac-out_SRCS}) target_compile_definitions(drumstick-rt-mac-out PRIVATE QT_PLUGIN) endif() target_link_libraries(drumstick-rt-mac-out PUBLIC Drumstick::RT Qt${QT_VERSION_MAJOR}::Core "-framework CoreMIDI -framework CoreFoundation -framework CoreServices -framework Foundation" ) target_include_directories(drumstick-rt-mac-out PRIVATE ${Drumstick_SOURCE_DIR}/library/include ../common ) set_target_properties(drumstick-rt-mac-out PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install(TARGETS drumstick-rt-mac-out EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR}) drumstick-2.9.0/library/rt-backends/mac-out/mac-out.pro0000644000175000017500000000102314541630232022024 0ustar pedropedroTEMPLATE = lib CONFIG += c++11 plugin static { CONFIG += staticlib create_prl } TARGET = drumstick-rt-mac-out DESTDIR = ../../../build/lib/drumstick2 DEPENDPATH += . ../../include ../common INCLUDEPATH += . ../../include ../common include (../../../global.pri) QT -= gui HEADERS += macmidioutput.h \ ../common/maccommon.h SOURCES += macmidioutput.cpp \ ../common/maccommon.cpp !static:LIBS += -F$$OUT_PWD/../../../build/lib -framework drumstick-rt LIBS += -framework CoreMIDI -framework CoreFoundation drumstick-2.9.0/library/rt-backends/mac-out/macmidioutput.cpp0000644000175000017500000002664014541630232023341 0ustar pedropedro/* Drumstick RT Backend Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "macmidioutput.h" #include "maccommon.h" #include #include #include #include #include #include #include namespace drumstick { namespace rt { static CFStringRef DEFAULT_PUBLIC_NAME CFSTR("MIDI Out"); class MacMIDIOutput::MacMIDIOutputPrivate { public: MIDIClientRef m_client; MIDIPortRef m_port; MIDIEndpointRef m_endpoint; MIDIEndpointRef m_destination; bool m_clientFilter; MIDIConnection m_currentOutput; QString m_publicName; QStringList m_excludedNames; QList m_outputDevices; bool m_status; QStringList m_diagnostics; MacMIDIOutputPrivate(): m_client(0), m_port(0), m_endpoint(0), m_destination(0), m_clientFilter(true), m_publicName(QString::fromCFString(DEFAULT_PUBLIC_NAME)) { internalCreate(DEFAULT_PUBLIC_NAME); } void registerStatus(const QString& context, const OSStatus status) { if (status != noErr) { m_diagnostics << QString("%1 error: %2").arg(context, status); m_diagnostics << getErrorTextFromOSStatus(status); } } void internalCreate(CFStringRef name) { OSStatus result = noErr; m_status = false; m_diagnostics.clear(); result = MIDIClientCreate( name, NULL, NULL, &m_client ); if ( result != noErr ) { registerStatus("MIDIClientCreate()", result); return; } result = MIDISourceCreate( m_client, name, &m_endpoint ); if ( result != noErr ) { registerStatus("MIDISourceCreate()", result); return; } result = MIDIOutputPortCreate( m_client, name, &m_port ); if (result != noErr) { registerStatus("MIDIOutputPortCreate()", result); return; } reloadDeviceList(true); m_status = (result == noErr); } virtual ~MacMIDIOutputPrivate() { internalDispose(); } void internalDispose() { OSStatus result = noErr; m_status = false; m_diagnostics.clear(); if (m_port != 0) { result = MIDIPortDispose( m_port ); if (result != noErr) { registerStatus("MIDIPortDispose()", result); m_port = 0; } } if (m_endpoint != 0) { result = MIDIEndpointDispose( m_endpoint ); if (result != noErr) { registerStatus("MIDIEndpointDispose()", result); m_endpoint = 0; } } if (m_client != 0) { result = MIDIClientDispose( m_client ); if (result != noErr) { registerStatus("MIDIClientDispose()", result); m_client = 0; } } m_status = (result == noErr); } void setPublicName(QString name) { if (m_publicName != name) { internalDispose(); internalCreate(name.toCFString()); m_publicName = name; } } void reloadDeviceList(bool advanced) { int num = MIDIGetNumberOfDestinations(); m_clientFilter = !advanced; m_outputDevices.clear(); for (int i = 0; i < num; ++i) { bool excluded = false; MIDIEndpointRef dest = MIDIGetDestination( i ); if (dest != 0) { QString name = getEndpointName(dest); if ( m_clientFilter && name.contains(QStringLiteral("IAC"), Qt::CaseSensitive) ) continue; if ( name.contains (m_publicName) ) continue; foreach ( const QString& n, m_excludedNames ) { if ( name.contains(n) ) { excluded = true; break; } } if (!excluded) { m_outputDevices << MIDIConnection(name, i); } } } if (!m_currentOutput.first.isEmpty() && m_destination != 0 && !m_outputDevices.contains(m_currentOutput)) { m_currentOutput = MIDIConnection(); m_destination = 0; } } void sendEvents( const MIDIPacketList* events ) { MIDIReceived(m_endpoint, events); if (m_destination != 0) MIDISend(m_port, m_destination, events); } bool open(const MIDIConnection& conn) { m_destination = MIDIGetDestination( conn.second.toInt() ); m_currentOutput = conn; return true; } void close() { m_destination = 0; m_currentOutput = MIDIConnection(); } }; MacMIDIOutput::MacMIDIOutput(QObject *parent) : MIDIOutput(parent), d(new MacMIDIOutputPrivate) { } MacMIDIOutput::~MacMIDIOutput() { delete d; } void MacMIDIOutput::initialize(QSettings *settings) { Q_UNUSED(settings) } QString MacMIDIOutput::backendName() { return QLatin1String("CoreMIDI"); } QString MacMIDIOutput::publicName() { return d->m_publicName; } void MacMIDIOutput::setPublicName(QString name) { d->setPublicName(name); } QList MacMIDIOutput::connections(bool advanced) { d->reloadDeviceList(advanced); return d->m_outputDevices; } void MacMIDIOutput::setExcludedConnections(QStringList conns) { d->m_excludedNames = conns; } void MacMIDIOutput::open(const MIDIConnection& name) { d->open(name); } void MacMIDIOutput::close() { d->close(); } MIDIConnection MacMIDIOutput::currentConnection() { return d->m_currentOutput; } /* Realtime MIDI slots */ void MacMIDIOutput::sendNoteOn(int chan, int note, int vel) { quint8 data[3]; MIDIPacketList pktlist ; MIDIPacket* packet = MIDIPacketListInit(&pktlist); data[0] = MIDI_STATUS_NOTEON | (chan & 0x0f); data[1] = note; data[2] = vel; packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0, sizeof(data), data); if (packet != NULL) d->sendEvents(&pktlist); } void MacMIDIOutput::sendNoteOff(int chan, int note, int vel) { quint8 data[3]; MIDIPacketList pktlist ; MIDIPacket* packet = MIDIPacketListInit(&pktlist); data[0] = MIDI_STATUS_NOTEOFF | (chan & 0x0f); data[1] = note; data[2] = vel; packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0, sizeof(data), data); if (packet != NULL) d->sendEvents(&pktlist); } void MacMIDIOutput::sendController(int chan, int control, int value) { quint8 data[3]; MIDIPacketList pktlist ; MIDIPacket* packet = MIDIPacketListInit(&pktlist); data[0] = MIDI_STATUS_CONTROLCHANGE | (chan & 0x0f); data[1] = control; data[2] = value; packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0, sizeof(data), data); if (packet != NULL) d->sendEvents(&pktlist); } void MacMIDIOutput::sendKeyPressure(int chan, int note, int value) { quint8 data[3]; MIDIPacketList pktlist ; MIDIPacket* packet = MIDIPacketListInit(&pktlist); data[0] = MIDI_STATUS_KEYPRESURE | (chan & 0x0f); data[1] = note; data[2] = value; packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0, sizeof(data), data); if (packet != NULL) d->sendEvents(&pktlist); } void MacMIDIOutput::sendProgram(int chan, int program) { quint8 data[2]; MIDIPacketList pktlist ; MIDIPacket* packet = MIDIPacketListInit(&pktlist); data[0] = MIDI_STATUS_PROGRAMCHANGE | (chan & 0x0f); data[1] = program; packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0, sizeof(data), data); if (packet != NULL) d->sendEvents(&pktlist); } void MacMIDIOutput::sendChannelPressure(int chan, int value) { quint8 data[2]; MIDIPacketList pktlist ; MIDIPacket* packet = MIDIPacketListInit(&pktlist); data[0] = MIDI_STATUS_CHANNELPRESSURE | (chan & 0x0f); data[1] = value; packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0, sizeof(data), data); if (packet != NULL) d->sendEvents(&pktlist); } void MacMIDIOutput::sendPitchBend(int chan, int value) { quint16 val = value + 8192; // value between -8192 and +8191 quint8 data[3]; MIDIPacketList pktlist ; MIDIPacket* packet = MIDIPacketListInit(&pktlist); data[0] = MIDI_STATUS_PITCHBEND | (chan & 0x0f); data[1] = MIDI_LSB(val); // LSB data[2] = MIDI_MSB(val); // MSB packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0, sizeof(data), data); if (packet != NULL) d->sendEvents(&pktlist); } void MacMIDIOutput::sendSysex(const QByteArray& data) { quint8 buf[4096]; if (data.size() > 4096) return; MIDIPacketList* pktlist = (MIDIPacketList*) &buf; MIDIPacket* packet = MIDIPacketListInit(pktlist); packet = MIDIPacketListAdd(pktlist, sizeof(buf), packet, 0, data.size(), (const Byte*)data.data()); if (packet != NULL) d->sendEvents(pktlist); } void MacMIDIOutput::sendSystemMsg(const int status) { quint8 data; MIDIPacketList pktlist; MIDIPacket* packet = MIDIPacketListInit(&pktlist); data = status; packet = MIDIPacketListAdd(&pktlist, sizeof(pktlist), packet, 0, sizeof(data), &data); if (packet != NULL) d->sendEvents(&pktlist); } QStringList MacMIDIOutput::getDiagnostics() { return d->m_diagnostics; } bool MacMIDIOutput::getStatus() { return d->m_status; } }} drumstick-2.9.0/library/rt-backends/mac-out/macmidioutput.h0000644000175000017500000000474514541630232023010 0ustar pedropedro/* Drumstick RT Backend Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef MACMIDIOUTPUT_H #define MACMIDIOUTPUT_H #include namespace drumstick { namespace rt { class MacMIDIOutput : public MIDIOutput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIOutput/2.0") Q_INTERFACES(drumstick::rt::MIDIOutput) Q_PROPERTY(QStringList diagnostics READ getDiagnostics) Q_PROPERTY(bool status READ getStatus); public: explicit MacMIDIOutput(QObject *parent = nullptr); virtual ~MacMIDIOutput(); // MIDIOutput interface virtual void initialize(QSettings* settings); virtual QString backendName(); virtual QString publicName(); virtual void setPublicName(QString name); virtual QList connections(bool advanced); virtual void setExcludedConnections(QStringList conns); virtual void open(const MIDIConnection& name); virtual void close(); virtual MIDIConnection currentConnection(); public Q_SLOTS: virtual void sendNoteOff(int chan, int note, int vel); virtual void sendNoteOn(int chan, int note, int vel); virtual void sendKeyPressure(int chan, int note, int value); virtual void sendController(int chan, int control, int value); virtual void sendProgram(int chan, int program); virtual void sendChannelPressure(int chan, int value); virtual void sendPitchBend(int chan, int value); virtual void sendSysex(const QByteArray &data); virtual void sendSystemMsg(const int status); private: class MacMIDIOutputPrivate; MacMIDIOutputPrivate* const d; QStringList getDiagnostics(); bool getStatus(); }; }} #endif // MACMIDIOUTPUT_H drumstick-2.9.0/library/rt-backends/macsynth/0000755000175000017500000000000014541630232020222 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/macsynth/CMakeLists.txt0000644000175000017500000000510214541630232022760 0ustar pedropedro# MIDI Sequencer C++ Library # Copyright (C) 2005-2023 Pedro Lopez-Cabanillas # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-rt-macsynth_QTOBJ_SRCS macsynth.h ) set(drumstick-rt-macsynth_SRCS ../common/maccommon.h ../common/maccommon.mm macsynth.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt-macsynth_MOC_SRCS ${drumstick-rt-macsynth_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp(drumstick-rt-macsynth_MOC_SRCS ${drumstick-rt-macsynth_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library(drumstick-rt-macsynth STATIC ${drumstick-rt-macsynth_MOC_SRCS} ${drumstick-rt-macsynth_SRCS}) target_compile_definitions(drumstick-rt-macsynth PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-macsynth PROPERTIES STATIC_LIB "libdrumstick-rt-macsynth") else() add_library(drumstick-rt-macsynth MODULE ${drumstick-rt-macsynth_MOC_SRCS} ${drumstick-rt-macsynth_SRCS}) target_compile_definitions(drumstick-rt-macsynth PRIVATE QT_PLUGIN) endif() target_link_libraries(drumstick-rt-macsynth PUBLIC Drumstick::RT Qt${QT_VERSION_MAJOR}::Core "-framework CoreMIDI -framework CoreFoundation -framework CoreServices -framework Foundation -framework CoreAudio -framework AudioToolbox -framework AudioUnit" ) target_include_directories(drumstick-rt-macsynth PRIVATE ${Drumstick_SOURCE_DIR}/library/include ../common ) set_target_properties(drumstick-rt-macsynth PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install(TARGETS drumstick-rt-macsynth EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR}) drumstick-2.9.0/library/rt-backends/macsynth/macsynth.pro0000644000175000017500000000101714541630232022571 0ustar pedropedroTEMPLATE = lib CONFIG += c++11 plugin static { CONFIG += staticlib create_prl } TARGET = drumstick-rt-macsynth DESTDIR = ../../../build/lib/drumstick2 DEPENDPATH += . ../../include INCLUDEPATH += . ../../include include (../../../global.pri) QT -= gui HEADERS += macsynth.h SOURCES += macsynth.cpp !static:LIBS += -F$$OUT_PWD/../../../build/lib -framework drumstick-rt LIBS += -framework CoreMIDI -framework CoreFoundation -framework CoreServices LIBS += -framework CoreAudio -framework AudioToolbox -framework AudioUnit drumstick-2.9.0/library/rt-backends/macsynth/macsynth.cpp0000644000175000017500000003337614541630232022570 0ustar pedropedro/* Drumstick RT Mac OSX Backend Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "macsynth.h" #include "maccommon.h" #include #include #include #define PRETTY_NAME "DLS Synth" namespace drumstick { namespace rt { class MacSynthOutput::MacSynthOutputPrivate { private: AUGraph m_graph; AudioUnit m_synthUnit; MIDIConnection m_connection; public: QString m_soundfont_dls; bool m_default_dls; bool m_reverb_dls; bool m_status; QStringList m_diagnostics; public: explicit MacSynthOutputPrivate(): m_graph(nullptr), m_synthUnit(nullptr), m_default_dls(true), m_reverb_dls(false) { //qDebug() << Q_FUNC_INFO; m_connection = MIDIConnection(); } ~MacSynthOutputPrivate() { //qDebug() << Q_FUNC_INFO; stop(); } void registerStatus(const QString& context, const OSStatus status) { if (status != noErr) { m_diagnostics << QString("%1 error: %2").arg(context).arg(status); m_diagnostics << getErrorTextFromOSStatus(status); } } bool useDefaultDls() const { return m_default_dls; } void setDefaultDlsFlag(const bool f) { if (f != m_default_dls) { m_default_dls = f; } } bool useReverb() const { return m_reverb_dls; } void setReverbFlag(const bool f) { if (f != m_reverb_dls) { m_reverb_dls = f; } } QString soundFontDls() const { return m_soundfont_dls; } void setSoundFontDls(const QString& sf) { if (sf != m_soundfont_dls) { m_soundfont_dls = sf; } } MIDIConnection currentConnection() { return m_connection; } void start () { OSStatus result; AudioComponentDescription cd; UInt32 usesReverb; AUNode synthNode = 0; AUNode outputNode = 0; AUNode limiterNode = 0; m_status = false; m_diagnostics.clear(); //qDebug() << Q_FUNC_INFO; if (m_graph == nullptr) { cd.componentManufacturer = kAudioUnitManufacturer_Apple; cd.componentFlags = 0; cd.componentFlagsMask = 0; result = NewAUGraph (&m_graph); registerStatus("NewAUGraph", result); if (result != noErr) return; cd.componentType = kAudioUnitType_MusicDevice; cd.componentSubType = kAudioUnitSubType_DLSSynth; result = AUGraphAddNode (m_graph, &cd, &synthNode); registerStatus( "AUGraphAddNode", result); cd.componentType = kAudioUnitType_Effect; cd.componentSubType = kAudioUnitSubType_PeakLimiter; result = AUGraphAddNode(m_graph, &cd, &limiterNode); registerStatus( "AUGraphAddNode", result); cd.componentType = kAudioUnitType_Output; cd.componentSubType = kAudioUnitSubType_DefaultOutput; result = AUGraphAddNode (m_graph, &cd, &outputNode); registerStatus( "AUGraphAddNode", result); result = AUGraphOpen (m_graph); registerStatus( "AUGraphOpen", result); if (result != noErr) return; result = AUGraphConnectNodeInput (m_graph, synthNode, 0, limiterNode, 0); registerStatus( "AUGraphConnectNodeInput", result); result = AUGraphConnectNodeInput (m_graph, limiterNode, 0, outputNode, 0); registerStatus( "AUGraphConnectNodeInput", result); result = AUGraphNodeInfo (m_graph, synthNode, nullptr, &m_synthUnit); registerStatus( "AUGraphNodeInfo", result); if (!m_default_dls && !m_soundfont_dls.isEmpty()) { QByteArray utf8file = m_soundfont_dls.toUtf8(); CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, reinterpret_cast(utf8file.data()), utf8file.length(), false); if (url) { // kMusicDeviceProperty_SoundBankURL since 10.5 result = AudioUnitSetProperty(m_synthUnit, kMusicDeviceProperty_SoundBankURL, kAudioUnitScope_Global, 0, &url, sizeof(url)); registerStatus( "AudioUnitSetProperty(SoundBankURL)", result); CFRelease(url); if (result != noErr) { return; } } } usesReverb = (m_reverb_dls ? 1 : 0); //qDebug() << "usesReverb =" << usesReverb; result = AudioUnitSetProperty ( m_synthUnit, kMusicDeviceProperty_UsesInternalReverb, kAudioUnitScope_Global, 0, &usesReverb, sizeof (usesReverb) ); registerStatus( "AudioUnitSetProperty(UsesInternalReverb)", result); result = AUGraphInitialize (m_graph); registerStatus( "AUGraphInitialize", result); if (result != noErr) { return; } for (uint i = 0; i < 16; ++i) { result = MusicDeviceMIDIEvent(m_synthUnit, MIDI_STATUS_CONTROLCHANGE+i, MIDI_CONTROL_MSB_MAIN_VOLUME,100,0); registerStatus( "MusicDeviceMIDIEvent", result ); result = MusicDeviceMIDIEvent(m_synthUnit, MIDI_STATUS_CONTROLCHANGE+i, MIDI_CONTROL_REVERB_SEND,100,0); registerStatus( "MusicDeviceMIDIEvent", result ); result = MusicDeviceMIDIEvent(m_synthUnit, MIDI_STATUS_CONTROLCHANGE+i, MIDI_CONTROL_MSB_BANK_SELECT,0,0); registerStatus( "MusicDeviceMIDIEvent", result ); result = MusicDeviceMIDIEvent(m_synthUnit, MIDI_STATUS_CONTROLCHANGE+i, MIDI_CONTROL_LSB_BANK_SELECT,0,0); registerStatus( "MusicDeviceMIDIEvent", result ); result = MusicDeviceMIDIEvent(m_synthUnit, MIDI_STATUS_PROGRAMCHANGE+i, 0,0,0); registerStatus( "MusicDeviceMIDIEvent", result ); } result = AUGraphStart (m_graph); registerStatus( "AUGraphStart", result); if (result != noErr) { return; } } m_status = (result == noErr); m_connection = MIDIConnection(QStringLiteral(PRETTY_NAME), QStringLiteral(PRETTY_NAME)); } void stop () { OSStatus result; m_status = false; m_diagnostics.clear(); //qDebug() << Q_FUNC_INFO; if (m_graph != nullptr) { result = AUGraphStop(m_graph); if (result != noErr) { registerStatus("AUGraphStop()", result); } result = AUGraphClose(m_graph); if (result != noErr) { registerStatus("AUGraphClose()", result); } result = DisposeAUGraph(m_graph); if (result != noErr) { registerStatus("DisposeAUGraph()", result); } m_graph = nullptr; } m_connection = MIDIConnection(); m_status = true; } void initialize ( QSettings *settings ) { //qDebug() << Q_FUNC_INFO; settings->beginGroup(PRETTY_NAME); m_default_dls = settings->value("default_dls", true).toBool(); m_reverb_dls = settings->value("reverb_dls", false).toBool(); m_soundfont_dls = settings->value("soundfont_dls").toString(); //qDebug() << "default_dls:" << m_default_dls; //qDebug() << "reverb_dls:" << m_reverb_dls; //qDebug() << "soundfont_dls" << m_soundfont_dls; settings->endGroup(); } void sendStatusEvent(uint status, uint data1, uint data2) { MusicDeviceMIDIEvent ( m_synthUnit, status, data1, data2, 0 ); } void sendSysexEvent(Byte *msg, uint msglen) { MusicDeviceSysEx ( m_synthUnit, msg, msglen ); } }; MacSynthOutput::MacSynthOutput(QObject *parent): MIDIOutput(parent), d(new MacSynthOutputPrivate) { } MacSynthOutput::~MacSynthOutput() { delete d; } void MacSynthOutput::initialize(QSettings* settings) { d->initialize(settings); } QString MacSynthOutput::backendName() { return QStringLiteral(PRETTY_NAME); } QString MacSynthOutput::publicName() { return QStringLiteral(PRETTY_NAME); } void MacSynthOutput::setPublicName(QString name) { Q_UNUSED(name) } QList MacSynthOutput::connections(bool advanced) { Q_UNUSED(advanced) return QList{MIDIConnection(QStringLiteral(PRETTY_NAME),QStringLiteral(PRETTY_NAME))}; } void MacSynthOutput::setExcludedConnections(QStringList conns) { Q_UNUSED(conns) } void MacSynthOutput::open(const MIDIConnection& name) { Q_UNUSED(name) //qDebug() << Q_FUNC_INFO; d->start(); } void MacSynthOutput::close() { //qDebug() << Q_FUNC_INFO; d->stop(); } MIDIConnection MacSynthOutput::currentConnection() { return d->currentConnection(); } void MacSynthOutput::sendNoteOn(int chan, int note, int vel) { uint status, data1, data2; status = MIDI_STATUS_NOTEON | (chan & 0x0f); data1 = static_cast(note); data2 = static_cast(vel); d->sendStatusEvent(status, data1, data2); } void MacSynthOutput::sendNoteOff(int chan, int note, int vel) { uint status, data1, data2; status = MIDI_STATUS_NOTEOFF | (chan & 0x0f); data1 = static_cast(note); data2 = static_cast(vel); d->sendStatusEvent(status, data1, data2); } void MacSynthOutput::sendController(int chan, int control, int value) { uint status, data1, data2; status = MIDI_STATUS_CONTROLCHANGE | (chan & 0x0f); data1 = static_cast(control); data2 = static_cast(value); d->sendStatusEvent(status, data1, data2); } void MacSynthOutput::sendKeyPressure(int chan, int note, int value) { uint status, data1, data2; status = MIDI_STATUS_KEYPRESURE | (chan & 0x0f); data1 = static_cast(note); data2 = static_cast(value); d->sendStatusEvent(status, data1, data2); } void MacSynthOutput::sendProgram(int chan, int program) { uint status, data1, data2; status = MIDI_STATUS_PROGRAMCHANGE | (chan & 0x0f); data1 = static_cast(program); data2 = 0; d->sendStatusEvent(status, data1, data2); } void MacSynthOutput::sendChannelPressure(int chan, int value) { uint status, data1, data2; status = MIDI_STATUS_CHANNELPRESSURE | (chan & 0x0f); data1 = static_cast(value); data2 = 0; d->sendStatusEvent(status, data1, data2); } void MacSynthOutput::sendPitchBend(int chan, int value) { uint status, data1, data2; quint16 val = static_cast(value + 8192); // value between -8192 and +8191 status = MIDI_STATUS_PITCHBEND | (chan & 0x0f); data1 = MIDI_LSB(val); // LSB data2 = MIDI_MSB(val); // MSB d->sendStatusEvent(status, data1, data2); } void MacSynthOutput::sendSysex(const QByteArray& data) { d->sendSysexEvent(reinterpret_cast(const_cast(data.data())), static_cast(data.length())); } void MacSynthOutput::sendSystemMsg(const int status) { d->sendStatusEvent(static_cast(status), 0, 0); } void MacSynthOutput::writeSettings(QSettings *settings) { settings->beginGroup("DLS Synth"); settings->setValue("soundfont_dls", d->m_soundfont_dls); settings->setValue("reverb_dls", d->m_reverb_dls); settings->setValue("default_dls", d->m_default_dls); settings->endGroup(); settings->sync(); } QStringList MacSynthOutput::getDiagnostics() { return d->m_diagnostics; } bool MacSynthOutput::getStatus() { return d->m_status; } }} drumstick-2.9.0/library/rt-backends/macsynth/macsynth.h0000644000175000017500000000512514541630232022224 0ustar pedropedro/* Drumstick RT Mac OSX Backend Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DLSSOFTSYNTH_H #define DLSSOFTSYNTH_H #include #include #include namespace drumstick { namespace rt { class MacSynthOutput : public MIDIOutput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIOutput/2.0") Q_INTERFACES(drumstick::rt::MIDIOutput) Q_PROPERTY(QStringList diagnostics READ getDiagnostics) Q_PROPERTY(bool status READ getStatus); public: explicit MacSynthOutput(QObject *parent = nullptr); virtual ~MacSynthOutput(); // MIDIOutput interface public: virtual void initialize(QSettings* settings); virtual QString backendName(); virtual QString publicName(); virtual void setPublicName(QString name); virtual QList connections(bool advanced); virtual void setExcludedConnections(QStringList conns); virtual void open(const MIDIConnection& name); virtual void close(); virtual MIDIConnection currentConnection(); public Q_SLOTS: virtual void sendNoteOff(int chan, int note, int vel); virtual void sendNoteOn(int chan, int note, int vel); virtual void sendKeyPressure(int chan, int note, int value); virtual void sendController(int chan, int control, int value); virtual void sendProgram(int chan, int program); virtual void sendChannelPressure(int chan, int value); virtual void sendPitchBend(int chan, int value); virtual void sendSysex(const QByteArray &data); virtual void sendSystemMsg(const int status); void writeSettings(QSettings *settings); private: class MacSynthOutputPrivate; MacSynthOutputPrivate* const d; QStringList getDiagnostics(); bool getStatus(); }; }} #endif // MACSYNTH_H drumstick-2.9.0/library/rt-backends/net-in/0000755000175000017500000000000014541630232017566 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/net-in/net-in.pro0000644000175000017500000000117014541630232021501 0ustar pedropedroTEMPLATE = lib CONFIG += c++11 plugin static { CONFIG += staticlib create_prl } TARGET = drumstick-rt-net-in DESTDIR = ../../../build/lib/drumstick2 DEPENDPATH += . ../../include ../common INCLUDEPATH += . ../../include ../common include (../../../global.pri) QT -= gui HEADERS += ../common/midiparser.h \ netmidiinput.h \ netmidiinput_p.h SOURCES += netmidiinput.cpp \ netmidiinput_p.cpp \ ../common/midiparser.cpp QT += network macx:!static:LIBS += -F$$OUT_PWD/../../../build/lib -framework drumstick-rt else:LIBS += -L$$OUT_PWD/../../../build/lib -l$$drumstickLib(drumstick-rt) drumstick-2.9.0/library/rt-backends/net-in/CMakeLists.txt0000644000175000017500000000513714541630232022334 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] find_package(Qt${QT_VERSION_MAJOR}Network REQUIRED) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-rt-net-in_QTOBJ_SRCS ../common/midiparser.h netmidiinput_p.h netmidiinput.h ) set(drumstick-rt-net-in_SRCS ../common/midiparser.cpp netmidiinput_p.cpp netmidiinput.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt-net-in_MOC_SRCS ${drumstick-rt-net-in_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp(drumstick-rt-net-in_MOC_SRCS ${drumstick-rt-net-in_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library(drumstick-rt-net-in STATIC ${drumstick-rt-net-in_MOC_SRCS} ${drumstick-rt-net-in_SRCS}) target_compile_definitions(drumstick-rt-net-in PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-net-in PROPERTIES STATIC_LIB "libdrumstick-rt-net-in") else() add_library(drumstick-rt-net-in MODULE ${drumstick-rt-net-in_MOC_SRCS} ${drumstick-rt-net-in_SRCS}) target_compile_definitions(drumstick-rt-net-in PRIVATE QT_PLUGIN) endif() target_include_directories(drumstick-rt-net-in PRIVATE ${Drumstick_SOURCE_DIR}/library/include ../common ) target_link_libraries(drumstick-rt-net-in PRIVATE Qt${QT_VERSION_MAJOR}::Network Drumstick::RT ) set_target_properties(drumstick-rt-net-in PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install(TARGETS drumstick-rt-net-in EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR}) drumstick-2.9.0/library/rt-backends/net-in/netmidiinput.cpp0000644000175000017500000000505314541630232023006 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "netmidiinput.h" #include "netmidiinput_p.h" #include namespace drumstick { namespace rt { const QString NetMIDIInput::DEFAULT_PUBLIC_NAME = QStringLiteral("MIDI In"); const QString NetMIDIInput::STR_ADDRESS_IPV4 = QStringLiteral("225.0.0.37"); const QString NetMIDIInput::STR_ADDRESS_IPV6 = QStringLiteral("ff12::37"); const int NetMIDIInput::MULTICAST_PORT = 21928; const int NetMIDIInput::LAST_PORT = 21948; NetMIDIInput::NetMIDIInput(QObject *parent): MIDIInput(parent), d(new NetMIDIInputPrivate(this)) {} void NetMIDIInput::writeSettings(QSettings *settings) { d->writeSettings(settings); } void NetMIDIInput::initialize(QSettings *settings) { d->initialize(settings); } QString NetMIDIInput::backendName() { return QStringLiteral("Network"); } QString NetMIDIInput::publicName() { return d->m_publicName; } void NetMIDIInput::setPublicName(QString name) { d->m_publicName = name; } QList NetMIDIInput::connections(bool advanced) { Q_UNUSED(advanced) return d->m_inputDevices; } void NetMIDIInput::setExcludedConnections(QStringList conns) { d->m_excludedNames = conns; } void NetMIDIInput::open(const MIDIConnection& name) { d->open(name); } void NetMIDIInput::close() { d->close(); } MIDIConnection NetMIDIInput::currentConnection() { return d->m_currentInput; } void NetMIDIInput::setMIDIThruDevice(MIDIOutput *device) { d->setMIDIThruDevice(device); } void NetMIDIInput::enableMIDIThru(bool enable) { d->m_thruEnabled = enable; } bool NetMIDIInput::isEnabledMIDIThru() { return d->m_thruEnabled && (d->m_out != nullptr); } QStringList NetMIDIInput::getDiagnostics() { return d->m_diagnostics; } bool NetMIDIInput::getStatus() { return d->m_status; } } // namespace rt } // namespace drumstick drumstick-2.9.0/library/rt-backends/net-in/netmidiinput.h0000644000175000017500000000501014541630232022444 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef NETMIDIINPUT_H #define NETMIDIINPUT_H #include #include #include #include namespace drumstick { namespace rt { class NetMIDIInputPrivate; class NetMIDIInput : public MIDIInput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIInput/2.0") Q_INTERFACES(drumstick::rt::MIDIInput) Q_PROPERTY(QStringList diagnostics READ getDiagnostics) Q_PROPERTY(bool status READ getStatus) public: explicit NetMIDIInput(QObject *parent = nullptr); public Q_SLOTS: void writeSettings(QSettings *settings); // MIDIInput interface public: virtual void initialize(QSettings* settings) override; virtual QString backendName() override; virtual QString publicName() override; virtual void setPublicName(QString name) override; virtual QList connections(bool advanced) override; virtual void setExcludedConnections(QStringList conns) override; virtual void open(const MIDIConnection& name) override; virtual void close() override; virtual MIDIConnection currentConnection() override; virtual void setMIDIThruDevice(MIDIOutput *device) override; virtual void enableMIDIThru(bool enable) override; virtual bool isEnabledMIDIThru() override; static const QString DEFAULT_PUBLIC_NAME; static const QString STR_ADDRESS_IPV4; static const QString STR_ADDRESS_IPV6; static const int MULTICAST_PORT; static const int LAST_PORT; private: NetMIDIInputPrivate * const d; private: QStringList getDiagnostics(); bool getStatus(); }; }} #endif // NETMIDIINPUT_H drumstick-2.9.0/library/rt-backends/net-in/netmidiinput_p.cpp0000644000175000017500000001140114541630232023317 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "netmidiinput.h" #include "netmidiinput_p.h" namespace drumstick { namespace rt { NetMIDIInputPrivate::NetMIDIInputPrivate(QObject *parent) : QObject(parent), m_inp(qobject_cast(parent)), m_out(nullptr), m_socket(nullptr), m_parser(nullptr), m_thruEnabled(false), m_port(0), m_publicName(NetMIDIInput::DEFAULT_PUBLIC_NAME), m_groupAddress(QHostAddress(NetMIDIInput::STR_ADDRESS_IPV4)), m_ipv6(false), m_status(false) { for(int i=NetMIDIInput::MULTICAST_PORT; i= NetMIDIInput::MULTICAST_PORT && p < NetMIDIInput::LAST_PORT && m_status) { //qDebug() << Q_FUNC_INFO << portName; m_socket = new QUdpSocket(); m_parser = new MIDIParser(m_inp); m_port = static_cast(p); m_currentInput = portName; bool res = m_socket->bind(m_ipv6 ? QHostAddress::AnyIPv6 : QHostAddress::AnyIPv4, m_port, QUdpSocket::ShareAddress); if (res) { #ifdef Q_OS_WIN // https://docs.microsoft.com/es-es/windows/desktop/WinSock/ip-multicast-2 m_socket->setSocketOption(QAbstractSocket::MulticastLoopbackOption, 0); #endif if (m_iface.isValid()) { res = m_socket->joinMulticastGroup(m_groupAddress, m_iface); } else { res = m_socket->joinMulticastGroup(m_groupAddress); } connect(m_socket, &QUdpSocket::readyRead, this, &NetMIDIInputPrivate::processIncomingMessages); m_status = m_socket->isValid(); } else { m_status = false; m_diagnostics << QString("Socket error. err: %1 = %2").arg(m_socket->error()).arg(m_socket->errorString()); } } } void NetMIDIInputPrivate::close() { delete m_socket; delete m_parser; m_socket = nullptr; m_parser = nullptr; m_currentInput = MIDIConnection(); m_status = false; m_diagnostics.clear(); } void NetMIDIInputPrivate::initialize(QSettings *settings) { if (settings != nullptr) { m_status = false; m_diagnostics.clear(); settings->beginGroup("Network"); QString ifaceName = settings->value("interface", QString()).toString(); m_ipv6 = settings->value("ipv6", false).toBool(); QString address = settings->value("address", m_ipv6 ? NetMIDIInput::STR_ADDRESS_IPV6 : NetMIDIInput::STR_ADDRESS_IPV4).toString(); settings->endGroup(); if (!ifaceName.isEmpty()) { m_iface = QNetworkInterface::interfaceFromName(ifaceName); } if (address.isEmpty()) { m_groupAddress.setAddress(m_ipv6 ? NetMIDIInput::STR_ADDRESS_IPV6 : NetMIDIInput::STR_ADDRESS_IPV4); } else { m_groupAddress.setAddress(address); } m_status = m_groupAddress.isMulticast(); if (!m_status) { m_diagnostics << QString("Invalid multicast address: %1").arg(address); } } } void NetMIDIInputPrivate::writeSettings(QSettings *settings) { if (settings != nullptr) { settings->beginGroup("Network"); settings->setValue("interface", m_iface.name()); settings->setValue("ipv6", m_ipv6); settings->setValue("address", m_groupAddress.toString()); settings->endGroup(); } } void NetMIDIInputPrivate::setMIDIThruDevice(MIDIOutput* device) { m_out = device; if (m_parser != nullptr) { m_parser->setMIDIThruDevice(device); } } void NetMIDIInputPrivate::processIncomingMessages() { while (m_socket->hasPendingDatagrams()) { QByteArray datagram; datagram.resize(static_cast(m_socket->pendingDatagramSize())); m_socket->readDatagram(datagram.data(), datagram.size()); if (m_parser != nullptr) { m_parser->parse(datagram); } } } } // namespace rt } // namespace drumstick drumstick-2.9.0/library/rt-backends/net-in/netmidiinput_p.h0000644000175000017500000000342714541630232022775 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef NETMIDIINPUT_P_H #define NETMIDIINPUT_P_H #include #include #include #include "midiparser.h" namespace drumstick { namespace rt { class MIDIOutput; class NetMIDIInput; class NetMIDIInputPrivate : public QObject { Q_OBJECT public: NetMIDIInput *m_inp; MIDIOutput *m_out; QUdpSocket *m_socket; MIDIParser *m_parser; int m_thruEnabled; quint16 m_port; QString m_publicName; QHostAddress m_groupAddress; MIDIConnection m_currentInput; QList m_inputDevices; QStringList m_excludedNames; QNetworkInterface m_iface; bool m_ipv6; bool m_status; QStringList m_diagnostics; explicit NetMIDIInputPrivate(QObject *parent = nullptr); void open(const MIDIConnection& conn); void close(); void initialize(QSettings* settings); void setMIDIThruDevice(MIDIOutput* device); void writeSettings(QSettings *settings); public Q_SLOTS: void processIncomingMessages(); }; }} #endif // NETMIDIINPUT_P_H drumstick-2.9.0/library/rt-backends/net-out/0000755000175000017500000000000014541630232017767 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/net-out/net-out.pro0000644000175000017500000000102714541630232022104 0ustar pedropedroTEMPLATE = lib CONFIG += c++11 plugin static { CONFIG += staticlib create_prl } TARGET = drumstick-rt-net-out DESTDIR = ../../../build/lib/drumstick2 DEPENDPATH += . ../../include INCLUDEPATH += . ../../include include (../../../global.pri) DEPENDPATH += ../../include INCLUDEPATH += ../../include QT -= gui HEADERS += netmidioutput.h SOURCES += netmidioutput.cpp QT += network macx:!static:LIBS += -F$$OUT_PWD/../../../build/lib -framework drumstick-rt else:LIBS += -L$$OUT_PWD/../../../build/lib -l$$drumstickLib(drumstick-rt) drumstick-2.9.0/library/rt-backends/net-out/CMakeLists.txt0000644000175000017500000000500214541630232022524 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] find_package(Qt${QT_VERSION_MAJOR}Network REQUIRED) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-rt-net-out_QTOBJ_SRCS netmidioutput.h ) set(drumstick-rt-net-out_SRCS netmidioutput.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt-net-out_MOC_SRCS ${drumstick-rt-net-out_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp(drumstick-rt-net-out_MOC_SRCS ${drumstick-rt-net-out_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library(drumstick-rt-net-out STATIC ${drumstick-rt-net-out_MOC_SRCS} ${drumstick-rt-net-out_SRCS}) target_compile_definitions(drumstick-rt-net-out PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-net-out PROPERTIES STATIC_LIB "libdrumstick-rt-net-out") else() add_library(drumstick-rt-net-out MODULE ${drumstick-rt-net-out_MOC_SRCS} ${drumstick-rt-net-out_SRCS}) target_compile_definitions(drumstick-rt-net-out PRIVATE QT_PLUGIN) endif() target_include_directories(drumstick-rt-net-out PRIVATE ${Drumstick_SOURCE_DIR}/library/include ) target_link_libraries(drumstick-rt-net-out PRIVATE Qt${QT_VERSION_MAJOR}::Network Drumstick::RT ) set_target_properties(drumstick-rt-net-out PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install(TARGETS drumstick-rt-net-out EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR}) drumstick-2.9.0/library/rt-backends/net-out/netmidioutput.cpp0000644000175000017500000002025214541630232023406 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "netmidioutput.h" #include #include #include namespace drumstick { namespace rt { const QString NetMIDIOutput::DEFAULT_PUBLIC_NAME = QStringLiteral("MIDI Out"); const QString NetMIDIOutput::STR_ADDRESS_IPV4 = QStringLiteral("225.0.0.37"); const QString NetMIDIOutput::STR_ADDRESS_IPV6 = QStringLiteral("ff12::37"); const int NetMIDIOutput::MULTICAST_PORT = 21928; const int NetMIDIOutput::LAST_PORT = 21948; class NetMIDIOutput::NetMIDIOutputPrivate { public: QUdpSocket *m_socket; QString m_publicName; QHostAddress m_groupAddress; MIDIConnection m_currentOutput; QList m_outputDevices; QStringList m_excludedNames; QNetworkInterface m_iface; quint16 m_port; bool m_ipv6; bool m_status; QStringList m_diagnostics; NetMIDIOutputPrivate() : m_socket(nullptr), m_publicName(DEFAULT_PUBLIC_NAME), m_groupAddress(QHostAddress(STR_ADDRESS_IPV4)), m_port(0), m_ipv6(false) { for(int i=MULTICAST_PORT; ibeginGroup("Network"); QString ifaceName = settings->value("interface", QString()).toString(); m_ipv6 = settings->value("ipv6", false).toBool(); QString address = settings->value("address", m_ipv6 ? STR_ADDRESS_IPV6 : STR_ADDRESS_IPV4).toString(); settings->endGroup(); if (!ifaceName.isEmpty()) { m_iface = QNetworkInterface::interfaceFromName(ifaceName); } if (address.isEmpty()) { m_groupAddress.setAddress(m_ipv6 ? STR_ADDRESS_IPV6 : STR_ADDRESS_IPV4); } else { m_groupAddress.setAddress(address); } m_status = m_groupAddress.isMulticast(); if (!m_status) { m_diagnostics << QString("Invalid multicast address: %1").arg(address); } } } void writeSettings(QSettings *settings) { if (settings != nullptr) { settings->beginGroup("Network"); settings->setValue("interface", m_iface.name()); settings->setValue("ipv6", m_ipv6); settings->setValue("address", m_groupAddress.toString()); settings->endGroup(); } } void open(const MIDIConnection& portName) { //qDebug() << Q_FUNC_INFO << portName; int p = portName.second.toInt(); if (p >= MULTICAST_PORT && p < LAST_PORT && m_status) { m_socket = new QUdpSocket(); bool res = m_socket->bind(m_ipv6 ? QHostAddress::AnyIPv6 : QHostAddress::AnyIPv4, m_socket->localPort()); if (res) { m_socket->setSocketOption(QAbstractSocket::MulticastTtlOption, 1); #ifdef Q_OS_UNIX m_socket->setSocketOption(QAbstractSocket::MulticastLoopbackOption, 0); #endif m_port = static_cast(p); if (m_iface.isValid()) { m_socket->setMulticastInterface(m_iface); } m_currentOutput = portName; m_status = m_socket->isValid(); } else { m_status = false; m_diagnostics << QString("Socket error: %1 = %2").arg(m_socket->error()).arg(m_socket->errorString()); } } } void close() { delete m_socket; m_socket = nullptr; m_currentOutput = MIDIConnection(); m_status = false; m_diagnostics.clear(); } void sendMessage(int m0) { QByteArray m; m.resize(1); m[0] = static_cast(m0); sendMessage(m); } void sendMessage(int m0, int m1) { QByteArray m; m.resize(2); m[0] = static_cast(m0); m[1] = static_cast(m1); sendMessage(m); } void sendMessage(int m0, int m1, int m2) { QByteArray m; m.resize(3); m[0] = static_cast(m0); m[1] = static_cast(m1); m[2] = static_cast(m2); sendMessage(m); } void sendMessage(const QByteArray& message ) { //qDebug() << Q_FUNC_INFO << message.toHex() << m_groupAddress << m_port; if (m_socket == nullptr) { m_diagnostics << "udp socket is null"; return; } else if (!m_socket->isValid() || m_socket->state() != QAbstractSocket::BoundState) { m_diagnostics << QString("udp socket has invalid state: %1 Error: %2 %3").arg(m_socket->state()).arg(m_socket->error()).arg(m_socket->errorString()); return; } auto res = m_socket->writeDatagram(message, m_groupAddress, m_port); //qDebug() << Q_FUNC_INFO << "writeDatagram:" << res; if (res < 0) { m_diagnostics << QString("Error: %1 %2").arg(m_socket->error()).arg(m_socket->errorString()); } } }; NetMIDIOutput::NetMIDIOutput(QObject *parent) : MIDIOutput(parent), d(new NetMIDIOutputPrivate) { } NetMIDIOutput::~NetMIDIOutput() { delete d; } void NetMIDIOutput::initialize(QSettings *settings) { d->initialize(settings); } QString NetMIDIOutput::backendName() { return QStringLiteral("Network"); } QString NetMIDIOutput::publicName() { return d->m_publicName; } void NetMIDIOutput::setPublicName(QString name) { d->m_publicName = name; } QList NetMIDIOutput::connections(bool advanced) { Q_UNUSED(advanced) return d->m_outputDevices; } void NetMIDIOutput::setExcludedConnections(QStringList conns) { Q_UNUSED(conns) } void NetMIDIOutput::open(const MIDIConnection& name) { d->open(name); } void NetMIDIOutput::close() { d->close(); } MIDIConnection NetMIDIOutput::currentConnection() { return d->m_currentOutput; } void NetMIDIOutput::sendNoteOff(int chan, int note, int vel) { d->sendMessage(MIDI_STATUS_NOTEOFF + chan, note, vel); } void NetMIDIOutput::sendNoteOn(int chan, int note, int vel) { d->sendMessage(MIDI_STATUS_NOTEON + chan, note, vel); } void NetMIDIOutput::sendKeyPressure(int chan, int note, int value) { d->sendMessage(MIDI_STATUS_KEYPRESURE + chan, note, value); } void NetMIDIOutput::sendController(int chan, int control, int value) { d->sendMessage(MIDI_STATUS_CONTROLCHANGE + chan, control, value); } void NetMIDIOutput::sendProgram(int chan, int program) { d->sendMessage(MIDI_STATUS_PROGRAMCHANGE + chan, program); } void NetMIDIOutput::sendChannelPressure(int chan, int value) { d->sendMessage(MIDI_STATUS_CHANNELPRESSURE + chan, value); } void NetMIDIOutput::sendPitchBend(int chan, int v) { // -8192 <= v <= 8191; 0 <= value <= 16384 int value = 8192 + v; d->sendMessage(MIDI_STATUS_PITCHBEND + chan, MIDI_LSB(value), MIDI_MSB(value)); } void NetMIDIOutput::sendSysex(const QByteArray &data) { d->sendMessage(data); } void NetMIDIOutput::sendSystemMsg(const int status) { d->sendMessage(status); } void NetMIDIOutput::writeSettings(QSettings *settings) { d->writeSettings(settings); } QStringList NetMIDIOutput::getDiagnostics() { return d->m_diagnostics; } bool NetMIDIOutput::getStatus() { return d->m_status; } } // namespace rt } // namespace drumstick drumstick-2.9.0/library/rt-backends/net-out/netmidioutput.h0000644000175000017500000000575414541630232023065 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef NETMIDIOUTPUT_H #define NETMIDIOUTPUT_H #include #include #include namespace drumstick { namespace rt { class NetMIDIOutput : public MIDIOutput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIOutput/2.0") Q_INTERFACES(drumstick::rt::MIDIOutput) Q_PROPERTY(QStringList diagnostics READ getDiagnostics) Q_PROPERTY(bool status READ getStatus) public: explicit NetMIDIOutput(QObject *parent = nullptr); virtual ~NetMIDIOutput(); static const QString DEFAULT_PUBLIC_NAME; static const QString STR_ADDRESS_IPV4; static const QString STR_ADDRESS_IPV6; static const int MULTICAST_PORT; static const int LAST_PORT; // MIDIOutput interface public: virtual void initialize(QSettings* settings) override; virtual QString backendName() override; virtual QString publicName() override; virtual void setPublicName(QString name) override; virtual QList connections(bool advanced) override; virtual void setExcludedConnections(QStringList conns) override; virtual void open(const MIDIConnection& name) override; virtual void close() override; virtual MIDIConnection currentConnection() override; public Q_SLOTS: virtual void sendNoteOff(int chan, int note, int vel) override; virtual void sendNoteOn(int chan, int note, int vel) override; virtual void sendKeyPressure(int chan, int note, int value) override; virtual void sendController(int chan, int control, int value) override; virtual void sendProgram(int chan, int program) override; virtual void sendChannelPressure(int chan, int value) override; virtual void sendPitchBend(int chan, int value) override; virtual void sendSysex(const QByteArray &data) override; virtual void sendSystemMsg(const int status) override; void writeSettings(QSettings *settings); private: class NetMIDIOutputPrivate; NetMIDIOutputPrivate * const d; private: QStringList getDiagnostics(); bool getStatus(); }; }} #endif // NETMIDIOUTPUT_H drumstick-2.9.0/library/rt-backends/oss-in/0000755000175000017500000000000014541630232017604 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/oss-in/oss-in.pro0000644000175000017500000000076514541630232021546 0ustar pedropedroTEMPLATE = lib TARGET = drumstick-rt-oss-in DESTDIR = ../../../build/lib/drumstick2 include (../../../global.pri) CONFIG += c++11 plugin static { CONFIG += staticlib create_prl } DEPENDPATH += ../../include ../common INCLUDEPATH += ../../include ../common QT -= gui HEADERS += ../common/midiparser.h \ ossinput_p.h \ ossinput.h SOURCES += ossinput.cpp \ ossinput_p.cpp \ ../common/midiparser.cpp LIBS += -L$$OUT_PWD/../../../build/lib -ldrumstick-rt drumstick-2.9.0/library/rt-backends/oss-in/ossinput.cpp0000644000175000017500000000400314541630232022171 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ossinput.h" namespace drumstick { namespace rt { const QString OSSInput::DEFAULT_PUBLIC_NAME = QStringLiteral("MIDI In"); OSSInput::OSSInput(QObject *parent) : MIDIInput(parent), d(new OSSInputPrivate(this)) { } OSSInput::~OSSInput() { delete d; } void OSSInput::initialize(QSettings *settings) { Q_UNUSED(settings) } QString OSSInput::backendName() { return "OSS"; } QString OSSInput::publicName() { return d->m_publicName; } void OSSInput::setPublicName(QString name) { d->m_publicName = name; } QList OSSInput::connections(bool advanced) { d->reloadDeviceList(advanced); return d->m_inputDevices; } void OSSInput::setExcludedConnections(QStringList conns) { Q_UNUSED(conns) } MIDIConnection OSSInput::currentConnection() { return d->m_currentInput; } void OSSInput::open(const MIDIConnection& conn) { d->open(conn); } void OSSInput::close() { d->close(); } void OSSInput::setMIDIThruDevice(MIDIOutput *device) { d->setMIDIThruDevice(device); //d->m_out = device; } void OSSInput::enableMIDIThru(bool enable) { d->m_thruEnabled = enable; } bool OSSInput::isEnabledMIDIThru() { return d->m_thruEnabled && (d->m_out != nullptr); } } // namespace rt } // namespace drumstick drumstick-2.9.0/library/rt-backends/oss-in/ossinput.h0000644000175000017500000000406714541630232021650 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ossINPUT_H #define ossINPUT_H #include #include #include #include "ossinput_p.h" namespace drumstick { namespace rt { class OSSInput : public MIDIInput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIInput/2.0") Q_INTERFACES(drumstick::rt::MIDIInput) public: explicit OSSInput(QObject *parent = nullptr); virtual ~OSSInput(); static const QString DEFAULT_PUBLIC_NAME; // MIDIInput interface public: virtual void initialize(QSettings* settings) override; virtual QString backendName() override; virtual QString publicName() override; virtual void setPublicName(QString name) override; virtual QList connections(bool advanced) override; virtual void setExcludedConnections(QStringList conns) override; virtual void open(const MIDIConnection& name) override; virtual void close() override; virtual MIDIConnection currentConnection() override; virtual void setMIDIThruDevice(MIDIOutput *device) override; virtual void enableMIDIThru(bool enable) override; virtual bool isEnabledMIDIThru() override; private: OSSInputPrivate *d; }; }} #endif // ossINPUT_H drumstick-2.9.0/library/rt-backends/oss-in/ossinput_p.cpp0000644000175000017500000000566514541630232022527 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include "ossinput.h" #include "ossinput_p.h" namespace drumstick { namespace rt { OSSInputPrivate::OSSInputPrivate(QObject *parent) : QObject(parent), m_inp(qobject_cast(parent)), m_out(nullptr), m_device(nullptr), m_notifier(nullptr), m_parser(nullptr), m_thruEnabled(false), m_advanced(false), m_publicName(OSSInput::DEFAULT_PUBLIC_NAME) { reloadDeviceList(); } void OSSInputPrivate::reloadDeviceList(bool advanced) { QDir dir("/dev"); QStringList filters; m_advanced = advanced; filters << "dmmidi*" << "admmidi*"; if (advanced) { filters << "midi*" << "amidi*"; } dir.setNameFilters(filters); dir.setFilter(QDir::System); dir.setSorting(QDir::Name); m_inputDevices.clear(); QFileInfoList listInfo = dir.entryInfoList(); foreach(const QFileInfo &info, listInfo) { m_inputDevices << MIDIConnection(info.baseName(), info.absoluteFilePath()); } } void OSSInputPrivate::open(const MIDIConnection& portName) { QFile *f = new QFile(portName.second.toString()); m_currentInput = portName; m_device = f; m_device->open( QIODevice::ReadOnly | QIODevice::Unbuffered ); m_notifier = new QSocketNotifier(f->handle(), QSocketNotifier::Read); m_parser = new MIDIParser(m_inp); m_buffer.clear(); connect(m_notifier, &QSocketNotifier::activated, this, &OSSInputPrivate::processIncomingMessages); //qDebug() << Q_FUNC_INFO << portName; } void OSSInputPrivate::close() { if (m_device != nullptr) { m_device->close(); delete m_notifier; delete m_device; delete m_parser; m_device = nullptr; m_parser = nullptr; } m_currentInput = MIDIConnection(); } void OSSInputPrivate::setMIDIThruDevice(MIDIOutput* device) { m_out = device; if (m_parser != nullptr) { m_parser->setMIDIThruDevice(device); } } void OSSInputPrivate::processIncomingMessages(int) { char ch; m_device->getChar(&ch); if (m_parser != nullptr) { uchar uch = static_cast(ch); m_parser->parse(uch); } } } // namespace rt } // namespace drumstick drumstick-2.9.0/library/rt-backends/oss-in/CMakeLists.txt0000644000175000017500000000502714541630232022350 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-rt-oss-in_QTOBJ_SRCS ../common/midiparser.h ossinput_p.h ossinput.h ) set(drumstick-rt-oss-in_SRCS ../common/midiparser.cpp ossinput_p.cpp ossinput.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt-oss-in_MOC_SRCS ${drumstick-rt-oss-in_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp(drumstick-rt-oss-in_MOC_SRCS ${drumstick-rt-oss-in_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library(drumstick-rt-oss-in STATIC ${drumstick-rt-oss-in_MOC_SRCS} ${drumstick-rt-oss-in_SRCS}) target_compile_definitions(drumstick-rt-oss-in PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-oss-in PROPERTIES STATIC_LIB "libdrumstick-rt-oss-in") else() add_library(drumstick-rt-oss-in MODULE ${drumstick-rt-oss-in_MOC_SRCS} ${drumstick-rt-oss-in_SRCS}) target_compile_definitions(drumstick-rt-oss-in PRIVATE QT_PLUGIN) endif() target_include_directories(drumstick-rt-oss-in PRIVATE ${Drumstick_SOURCE_DIR}/library/include ../common ) target_link_libraries(drumstick-rt-oss-in PRIVATE Qt${QT_VERSION_MAJOR}::Core Drumstick::RT ) set_target_properties(drumstick-rt-oss-in PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install(TARGETS drumstick-rt-oss-in EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR}) drumstick-2.9.0/library/rt-backends/oss-in/ossinput_p.h0000644000175000017500000000332214541630232022160 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef OSSINPUT_P_H #define OSSINPUT_P_H #include #include #include #include #include #include "midiparser.h" namespace drumstick { namespace rt { class MIDIOutput; class OSSInput; class OSSInputPrivate : public QObject { Q_OBJECT public: OSSInput *m_inp; MIDIOutput *m_out; QIODevice *m_device; QSocketNotifier *m_notifier; MIDIParser *m_parser; bool m_thruEnabled; bool m_advanced; QString m_publicName; MIDIConnection m_currentInput; QList m_inputDevices; QStringList m_excludedNames; QByteArray m_buffer; explicit OSSInputPrivate(QObject *parent = nullptr); void reloadDeviceList(bool advanced = false); void open(const MIDIConnection& portName); void close(); //void parse(); void setMIDIThruDevice(MIDIOutput* device); public Q_SLOTS: void processIncomingMessages(int); }; }} #endif // OSSINPUT_P_H drumstick-2.9.0/library/rt-backends/oss-out/0000755000175000017500000000000014541630232020005 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/oss-out/oss-out.pro0000644000175000017500000000054314541630232022142 0ustar pedropedroTEMPLATE = lib TARGET = drumstick-rt-oss-out DESTDIR = ../../../build/lib/drumstick2 include (../../../global.pri) CONFIG += c++11 plugin static { CONFIG += staticlib create_prl } DEPENDPATH += ../../include INCLUDEPATH += ../../include QT -= gui HEADERS += ossoutput.h SOURCES += ossoutput.cpp LIBS += -L$$OUT_PWD/../../../build/lib -ldrumstick-rt drumstick-2.9.0/library/rt-backends/oss-out/ossoutput.cpp0000644000175000017500000001211314541630232022574 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include "ossoutput.h" namespace drumstick { namespace rt { const QString OSSOutput::DEFAULT_PUBLIC_NAME = QStringLiteral("MIDI Out"); class OSSOutput::OSSOutputPrivate { public: bool m_advanced; QIODevice *m_device; QString m_publicName; MIDIConnection m_currentOutput; QList m_outputDevices; QStringList m_excludedNames; OSSOutputPrivate() : m_advanced(false), m_device(nullptr), m_publicName(DEFAULT_PUBLIC_NAME) { reloadDeviceList(); } ~OSSOutputPrivate() { close(); } void reloadDeviceList(bool advanced = false) { QDir dir("/dev"); QStringList filters; m_advanced = advanced; filters << "dmmidi*" << "admmidi*"; if (advanced) { filters << "midi*" << "amidi*"; } dir.setNameFilters(filters); dir.setFilter(QDir::System); dir.setSorting(QDir::Name); m_outputDevices.clear(); QFileInfoList listInfo = dir.entryInfoList(); foreach(const QFileInfo &info, listInfo) { m_outputDevices << MIDIConnection(info.baseName(), info.absoluteFilePath()); } } void open(const MIDIConnection& portName) { //qDebug() << Q_FUNC_INFO << portName; m_device = new QFile(portName.second.toString()); m_device->open(QIODevice::WriteOnly | QIODevice::Unbuffered); m_currentOutput = portName; } void close() { if (m_device != nullptr) { m_device->close(); delete m_device; m_device = nullptr; } m_currentOutput = MIDIConnection(); } void sendMessage(int m0) { QByteArray m; m.resize(1); m[0] = m0; sendMessage(m); } void sendMessage(int m0, int m1) { QByteArray m; m.resize(2); m[0] = m0; m[1] = m1; sendMessage(m); } void sendMessage(int m0, int m1, int m2) { QByteArray m; m.resize(3); m[0] = m0; m[1] = m1; m[2] = m2; sendMessage(m); } void sendMessage(const QByteArray& message ) { if (m_device == nullptr) { //qDebug() << "device is null"; return; } m_device->write(message); //m_device->flush(); } }; OSSOutput::OSSOutput(QObject *parent) : MIDIOutput(parent), d(new OSSOutputPrivate) {} OSSOutput::~OSSOutput() { delete d; } void OSSOutput::initialize(QSettings *settings) { Q_UNUSED(settings) } QString OSSOutput::backendName() { return QStringLiteral("OSS"); } QString OSSOutput::publicName() { return d->m_publicName; } void OSSOutput::setPublicName(QString name) { d->m_publicName = name; } QList OSSOutput::connections(bool advanced) { d->reloadDeviceList(advanced); return d->m_outputDevices; } void OSSOutput::setExcludedConnections(QStringList conns) { Q_UNUSED(conns) } void OSSOutput::open(const MIDIConnection& name) { d->open(name); } void OSSOutput::close() { d->close(); } MIDIConnection OSSOutput::currentConnection() { return d->m_currentOutput; } void OSSOutput::sendNoteOff(int chan, int note, int vel) { d->sendMessage(MIDI_STATUS_NOTEOFF + chan, note, vel);} void OSSOutput::sendNoteOn(int chan, int note, int vel) { d->sendMessage(MIDI_STATUS_NOTEON + chan, note, vel); } void OSSOutput::sendKeyPressure(int chan, int note, int value) { d->sendMessage(MIDI_STATUS_KEYPRESURE + chan, note, value); } void OSSOutput::sendController(int chan, int control, int value) { d->sendMessage(MIDI_STATUS_CONTROLCHANGE + chan, control, value); } void OSSOutput::sendProgram(int chan, int program) { d->sendMessage(MIDI_STATUS_PROGRAMCHANGE + chan, program); } void OSSOutput::sendChannelPressure(int chan, int value) { d->sendMessage(MIDI_STATUS_CHANNELPRESSURE + chan, value); } void OSSOutput::sendPitchBend(int chan, int v) { // -8192 <= v <= 8191; 0 <= value <= 16384 int value = 8192 + v; d->sendMessage(MIDI_STATUS_PITCHBEND + chan, MIDI_LSB(value), MIDI_MSB(value)); } void OSSOutput::sendSysex(const QByteArray &data) { d->sendMessage(data); } void OSSOutput::sendSystemMsg(const int status) { d->sendMessage(status); } } // namespace rt } // namespace drumstick drumstick-2.9.0/library/rt-backends/oss-out/CMakeLists.txt0000644000175000017500000000470314541630232022551 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-rt-oss-out_QTOBJ_SRCS ossoutput.h ) set(drumstick-rt-oss-out_SRCS ossoutput.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt-oss-out_MOC_SRCS ${drumstick-rt-oss-out_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp(drumstick-rt-oss-out_MOC_SRCS ${drumstick-rt-oss-out_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library(drumstick-rt-oss-out STATIC ${drumstick-rt-oss-out_MOC_SRCS} ${drumstick-rt-oss-out_SRCS}) target_compile_definitions(drumstick-rt-oss-out PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-oss-out PROPERTIES STATIC_LIB "libdrumstick-rt-oss-out") else() add_library(drumstick-rt-oss-out MODULE ${drumstick-rt-oss-out_MOC_SRCS} ${drumstick-rt-oss-out_SRCS}) target_compile_definitions(drumstick-rt-oss-out PRIVATE QT_PLUGIN) endif() target_include_directories(drumstick-rt-oss-out PRIVATE ${Drumstick_SOURCE_DIR}/library/include ) target_link_libraries(drumstick-rt-oss-out PRIVATE Qt${QT_VERSION_MAJOR}::Core Drumstick::RT ) set_target_properties(drumstick-rt-oss-out PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install(TARGETS drumstick-rt-oss-out EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR}) drumstick-2.9.0/library/rt-backends/oss-out/ossoutput.h0000644000175000017500000000502214541630232022242 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ossOUTPUT_H #define ossOUTPUT_H #include #include namespace drumstick { namespace rt { class OSSOutput : public MIDIOutput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIOutput/2.0") Q_INTERFACES(drumstick::rt::MIDIOutput) public: explicit OSSOutput(QObject *parent = nullptr); virtual ~OSSOutput(); static const QString DEFAULT_PUBLIC_NAME; // MIDIOutput interface public: virtual void initialize(QSettings* settings) override; virtual QString backendName() override; virtual QString publicName() override; virtual void setPublicName(QString name) override; virtual QList connections(bool advanced) override; virtual void setExcludedConnections(QStringList conns) override; virtual void open(const MIDIConnection& name) override; virtual void close() override; virtual MIDIConnection currentConnection() override; public Q_SLOTS: virtual void sendNoteOff(int chan, int note, int vel) override; virtual void sendNoteOn(int chan, int note, int vel) override; virtual void sendKeyPressure(int chan, int note, int value) override; virtual void sendController(int chan, int control, int value) override; virtual void sendProgram(int chan, int program) override; virtual void sendChannelPressure(int chan, int value) override; virtual void sendPitchBend(int chan, int value) override; virtual void sendSysex(const QByteArray &data) override; virtual void sendSystemMsg(const int status) override; private: class OSSOutputPrivate; OSSOutputPrivate *d; }; }} #endif // ossOUTPUT_H drumstick-2.9.0/library/rt-backends/win-in/0000755000175000017500000000000014541630232017575 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/win-in/CMakeLists.txt0000644000175000017500000000454314541630232022343 0ustar pedropedro# MIDI Sequencer C++ Library # Copyright (C) 2005-2023 Pedro Lopez-Cabanillas # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-rt-win-in_QTOBJ_SRCS winmidiinput.h ) set(drumstick-rt-win-in_SRCS winmidiinput.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt-win-in_MOC_SRCS ${drumstick-rt-win-in_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp(drumstick-rt-win-in_MOC_SRCS ${drumstick-rt-win-in_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library(drumstick-rt-win-in STATIC ${drumstick-rt-win-in_MOC_SRCS} ${drumstick-rt-win-in_SRCS}) target_compile_definitions(drumstick-rt-win-in PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-win-in PROPERTIES STATIC_LIB "libdrumstick-rt-win-in") else() add_library(drumstick-rt-win-in MODULE ${drumstick-rt-win-in_MOC_SRCS} ${drumstick-rt-win-in_SRCS}) target_compile_definitions(drumstick-rt-win-in PRIVATE QT_PLUGIN) endif() target_link_libraries(drumstick-rt-win-in PUBLIC Qt${QT_VERSION_MAJOR}::Core Drumstick::RT PRIVATE winmm ) target_include_directories(drumstick-rt-win-in PRIVATE ${Drumstick_SOURCE_DIR}/library/include) set_target_properties(drumstick-rt-win-in PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install(TARGETS drumstick-rt-win-in EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR}) drumstick-2.9.0/library/rt-backends/win-in/win-in.pro0000644000175000017500000000070414541630232021521 0ustar pedropedroTEMPLATE = lib CONFIG += c++11 plugin static { CONFIG += staticlib create_prl } TARGET = drumstick-rt-win-in DESTDIR = ../../../build/lib/drumstick2 DEPENDPATH += . ../../include INCLUDEPATH += . ../../include include (../../../global.pri) DEPENDPATH += ../../include INCLUDEPATH += ../../include QT -= gui HEADERS += winmidiinput.h SOURCES += winmidiinput.cpp LIBS += -lwinmm LIBS += -L$$OUT_PWD/../../../build/lib -l$$drumstickLib(drumstick-rt) drumstick-2.9.0/library/rt-backends/win-in/winmidiinput.h0000644000175000017500000000417614541630232022476 0ustar pedropedro/* Drumstick RT Windows Backend Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef WINMIDIINPUT_H #define WINMIDIINPUT_H #include #include #include namespace drumstick { namespace rt { class WinMIDIInput : public MIDIInput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIInput/2.0") Q_INTERFACES(drumstick::rt::MIDIInput) Q_PROPERTY(QStringList diagnostics READ getDiagnostics) Q_PROPERTY(bool status READ getStatus); public: class WinMIDIInputPrivate; explicit WinMIDIInput(QObject *parent = nullptr); virtual ~WinMIDIInput(); // MIDIInput interface public: virtual void initialize(QSettings* settings); virtual QString backendName(); virtual QString publicName(); virtual void setPublicName(QString name); virtual QList connections(bool advanced); virtual void setExcludedConnections(QStringList conns); virtual void open(const MIDIConnection& name); virtual void close(); virtual MIDIConnection currentConnection(); virtual void setMIDIThruDevice(MIDIOutput *device); virtual void enableMIDIThru(bool enable); virtual bool isEnabledMIDIThru(); private: WinMIDIInputPrivate * const d; private: QStringList getDiagnostics(); bool getStatus(); }; }} #endif // WINMIDIINPUT_H drumstick-2.9.0/library/rt-backends/win-in/winmidiinput.cpp0000644000175000017500000002512214541630232023023 0ustar pedropedro/* Drumstick RT Windows Backend Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include "winmidiinput.h" #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) #define right Qt::right #define left Qt::left #define hex Qt::hex #define dec Qt::dec #define endl Qt::endl #endif namespace drumstick { namespace rt { static QLatin1String DEFAULT_PUBLIC_NAME = QLatin1String("MIDI In"); void CALLBACK midiCallback( HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 ); class WinMIDIInput::WinMIDIInputPrivate { public: WinMIDIInput *m_inp; MIDIOutput *m_out; bool m_thruEnabled; bool m_clientFilter; HMIDIIN m_handle; QString m_publicName; MIDIConnection m_currentInput; QStringList m_excludedNames; QList m_inputDevices; bool m_status; QStringList m_diagnostics; explicit WinMIDIInputPrivate(WinMIDIInput *inp): m_inp(inp), m_out(nullptr), m_thruEnabled(false), m_clientFilter(true), m_handle(nullptr), m_publicName(DEFAULT_PUBLIC_NAME) { reloadDeviceList(true); } void open(const MIDIConnection &conn) { MMRESULT res; m_status = false; m_diagnostics.clear(); if (conn != m_currentInput) { if (m_handle != nullptr) close(); reloadDeviceList(!m_clientFilter); if (!conn.first.isEmpty()) { res = midiInOpen(&m_handle, conn.second.toInt(), (DWORD_PTR) midiCallback, (DWORD_PTR) this, CALLBACK_FUNCTION | MIDI_IO_STATUS ); if (res != MMSYSERR_NOERROR) { logError("midiInOpen()",res); } else { res = midiInStart(m_handle); if (res != MMSYSERR_NOERROR) { logError("midiInStart()",res); } } m_currentInput = conn; m_status = (res == MMSYSERR_NOERROR); } } } void close() { MMRESULT res; m_status = false; m_diagnostics.clear(); if (m_handle != nullptr) { res = midiInStop(m_handle); if (res != MMSYSERR_NOERROR) logError("midiInStop()", res); res = midiInReset( m_handle ); if (res != MMSYSERR_NOERROR) logError("midiInReset()", res); res = midiInClose( m_handle ); if (res != MMSYSERR_NOERROR) logError("midiInClose()", res); m_handle = nullptr; } m_currentInput = MIDIConnection(); } void reloadDeviceList(bool advanced) { MMRESULT res; MIDIINCAPS deviceCaps; QString devName; unsigned int dev, max = midiInGetNumDevs(); m_inputDevices.clear(); m_clientFilter = !advanced; for ( dev = 0; dev < max; ++dev) { bool excluded = false; res = midiInGetDevCaps( dev, &deviceCaps, sizeof(MIDIINCAPS)); if (res != MMSYSERR_NOERROR) break; #if defined(UNICODE) devName = QString::fromWCharArray(deviceCaps.szPname); #else devName = QString::fromLocal8Bit(deviceCaps.szPname); #endif foreach (const QString& n , m_excludedNames) { if (devName.startsWith(n)) { excluded = true; break; } } if (!excluded) { m_inputDevices << MIDIConnection(devName, dev); } } } void setPublicName(QString name) { if (m_publicName != name) { m_publicName = name; } } void emitSignals(int status, int channel, int data1, int data2) { switch (status) { case MIDI_STATUS_NOTEOFF: if(m_out != nullptr && m_thruEnabled) m_out->sendNoteOff(channel, data1, data2); emit m_inp->midiNoteOff(channel, data1, data2); break; case MIDI_STATUS_NOTEON: if(m_out != nullptr && m_thruEnabled) m_out->sendNoteOn(channel, data1, data2); emit m_inp->midiNoteOn(channel, data1, data2); break; case MIDI_STATUS_KEYPRESURE: if(m_out != nullptr && m_thruEnabled) m_out->sendKeyPressure(channel, data1, data2); emit m_inp->midiKeyPressure(channel, data1, data2); break; case MIDI_STATUS_CONTROLCHANGE: if(m_out != nullptr && m_thruEnabled) m_out->sendController(channel, data1, data2); emit m_inp->midiController(channel, data1, data2); break; case MIDI_STATUS_PROGRAMCHANGE: if(m_out != nullptr && m_thruEnabled) m_out->sendProgram(channel, data1); emit m_inp->midiProgram(channel, data1); break; case MIDI_STATUS_CHANNELPRESSURE: if(m_out != nullptr && m_thruEnabled) m_out->sendChannelPressure(channel, data1); emit m_inp->midiChannelPressure(channel, data1); break; case MIDI_STATUS_PITCHBEND: { int value = (data1 + data2 * 0x80) - 8192; if(m_out != nullptr && m_thruEnabled) m_out->sendPitchBend(channel, value); emit m_inp->midiPitchBend(channel, value); } break; default: m_diagnostics << QString("MIDI in status? %1").arg(status); } } void emitSysex(QByteArray data) { if(m_out != nullptr && m_thruEnabled) m_out->sendSysex(data); emit m_inp->midiSysex(data); } QString mmErrorString(MMRESULT err) { QString errstr; #ifdef UNICODE WCHAR buffer[1024]; midiInGetErrorText(err, &buffer[0], sizeof(buffer)); errstr = QString::fromWCharArray(buffer); #else char buffer[1024]; midiOutGetErrorText(err, &buffer[0], sizeof(buffer)); errstr = QString::fromLocal8Bit(buffer); #endif return errstr; } void logError(const QString context, const MMRESULT code) { m_diagnostics << QString("%1 error: %2 (%3)").arg(context).arg(code).arg(mmErrorString(code)); } }; void CALLBACK midiCallback( HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 ) { Q_UNUSED(hMidiIn) Q_UNUSED(dwParam2) WinMIDIInput::WinMIDIInputPrivate* object = (WinMIDIInput::WinMIDIInputPrivate*) dwInstance; switch( wMsg ) { case MIM_OPEN: object->m_diagnostics << "Open input"; break; case MIM_CLOSE: object->m_diagnostics << "Close input"; break; case MIM_ERROR: case MIM_LONGERROR: object->m_diagnostics << "Errors input"; break; case MIM_LONGDATA: object->m_diagnostics << "Sysex data input"; break; case MIM_DATA: case MIM_MOREDATA: { int status = dwParam1 & 0xf0; int channel = dwParam1 & 0x0f; int data1 = (dwParam1 & 0x7f00) >> 8; int data2 = (dwParam1 & 0x7f0000) >> 16; object->emitSignals(status, channel, data1, data2); } break; default: object->m_diagnostics << QString("unknown input message: %1").arg(wMsg); break; } } WinMIDIInput::WinMIDIInput(QObject *parent) : MIDIInput(parent), d(new WinMIDIInputPrivate(this)) { } WinMIDIInput::~WinMIDIInput() { delete d; } void WinMIDIInput::initialize(QSettings *settings) { Q_UNUSED(settings) } QString WinMIDIInput::backendName() { return QLatin1String("Windows MM"); } QString WinMIDIInput::publicName() { return d->m_publicName; } void WinMIDIInput::setPublicName(QString name) { d->setPublicName(name); } QList WinMIDIInput::connections(bool advanced) { d->reloadDeviceList(advanced); return d->m_inputDevices; } void WinMIDIInput::setExcludedConnections(QStringList conns) { d->m_excludedNames = conns; } void WinMIDIInput::open(const MIDIConnection& name) { d->open(name); } void WinMIDIInput::close() { d->close(); } MIDIConnection WinMIDIInput::currentConnection() { return d->m_currentInput; } void WinMIDIInput::setMIDIThruDevice(MIDIOutput *device) { d->m_out = device; } void WinMIDIInput::enableMIDIThru(bool enable) { d->m_thruEnabled = enable; } bool WinMIDIInput::isEnabledMIDIThru() { return d->m_thruEnabled && d->m_out != nullptr; } QStringList WinMIDIInput::getDiagnostics() { return d->m_diagnostics; } bool WinMIDIInput::getStatus() { return d->m_status; } }} // namespace drumstick::rt drumstick-2.9.0/library/rt-backends/win-out/0000755000175000017500000000000014541630232017776 5ustar pedropedrodrumstick-2.9.0/library/rt-backends/win-out/CMakeLists.txt0000644000175000017500000000457114541630232022545 0ustar pedropedro# MIDI Sequencer C++ Library # Copyright (C) 2005-2023 Pedro Lopez-Cabanillas # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-rt-win-out_QTOBJ_SRCS winmidioutput.h ) set(drumstick-rt-win-out_SRCS winmidioutput.cpp ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt-win-out_MOC_SRCS ${drumstick-rt-win-out_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) else() qt_wrap_cpp(drumstick-rt-win-out_MOC_SRCS ${drumstick-rt-win-out_QTOBJ_SRCS} OPTIONS -I ${Drumstick_SOURCE_DIR}/library/include) endif() if(STATIC_DRUMSTICK) add_library(drumstick-rt-win-out STATIC ${drumstick-rt-win-out_MOC_SRCS} ${drumstick-rt-win-out_SRCS}) target_compile_definitions(drumstick-rt-win-out PRIVATE QT_STATICPLUGIN) set_target_properties(drumstick-rt-win-out PROPERTIES STATIC_LIB "libdrumstick-rt-win-out") else() add_library(drumstick-rt-win-out MODULE ${drumstick-rt-win-out_MOC_SRCS} ${drumstick-rt-win-out_SRCS}) target_compile_definitions(drumstick-rt-win-out PRIVATE QT_PLUGIN) endif() target_include_directories(drumstick-rt-win-out PRIVATE ${Drumstick_SOURCE_DIR}/library/include) target_link_libraries(drumstick-rt-win-out PUBLIC Qt${QT_VERSION_MAJOR}::Core Drumstick::RT PRIVATE winmm ) set_target_properties(drumstick-rt-win-out PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/${DRUMSTICK_PLUGINS_DIR}) install(TARGETS drumstick-rt-win-out EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/${DRUMSTICK_PLUGINS_DIR}) drumstick-2.9.0/library/rt-backends/win-out/win-out.pro0000644000175000017500000000070714541630232022126 0ustar pedropedroTEMPLATE = lib CONFIG += c++11 plugin static { CONFIG += staticlib create_prl } TARGET = drumstick-rt-win-out DESTDIR = ../../../build/lib/drumstick2 DEPENDPATH += . ../../include INCLUDEPATH += . ../../include include (../../../global.pri) DEPENDPATH += ../../include INCLUDEPATH += ../../include QT -= gui HEADERS += winmidioutput.h SOURCES += winmidioutput.cpp LIBS += -lwinmm LIBS += -L$$OUT_PWD/../../../build/lib -l$$drumstickLib(drumstick-rt) drumstick-2.9.0/library/rt-backends/win-out/winmidioutput.cpp0000644000175000017500000002672714541630232023441 0ustar pedropedro/* Drumstick RT Windows Backend Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include "winmidioutput.h" #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) #define hex Qt::hex #endif namespace drumstick { namespace rt { union WinMIDIPacket { WinMIDIPacket() : dwPacket(0) {} DWORD dwPacket; quint8 data[sizeof(DWORD)]; }; static QLatin1String DEFAULT_PUBLIC_NAME = QLatin1String("MIDI Out"); void CALLBACK midiCallback( HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2); class WinMIDIOutput::WinMIDIOutputPrivate { public: HMIDIOUT m_handle; bool m_clientFilter; QString m_publicName; MIDIConnection m_currentOutput; QList m_outputDevices; MIDIHDR m_midiSysexHdr; QByteArray m_sysexBuffer; QStringList m_excludedNames; bool m_status; QStringList m_diagnostics; WinMIDIOutputPrivate(): m_handle(nullptr), m_clientFilter(true), m_publicName(DEFAULT_PUBLIC_NAME) { reloadDeviceList(true); } void reloadDeviceList(bool advanced) { MMRESULT res; MIDIOUTCAPS deviceCaps; QString devName; unsigned int dev, max = midiOutGetNumDevs(); m_outputDevices.clear(); m_clientFilter = !advanced; for ( dev = 0; dev < max; ++dev) { bool excluded = false; res = midiOutGetDevCaps( dev, &deviceCaps, sizeof(MIDIOUTCAPS)); if (res != MMSYSERR_NOERROR) break; if (m_clientFilter && (deviceCaps.wTechnology == MOD_MAPPER || dev == MIDI_MAPPER)) continue; #if defined(UNICODE) devName = QString::fromWCharArray(deviceCaps.szPname); #else devName = QString::fromLocal8Bit(deviceCaps.szPname); #endif foreach (const QString& n, m_excludedNames) { if (devName.startsWith(n)) { excluded = true; break; } } if (!excluded) { m_outputDevices << MIDIConnection(devName, dev); } } if (!m_clientFilter) { dev = MIDI_MAPPER; res = midiOutGetDevCaps( dev, &deviceCaps, sizeof(MIDIOUTCAPS)); if (res == MMSYSERR_NOERROR) { #if defined(UNICODE) devName = QString::fromWCharArray(deviceCaps.szPname); #else devName = QString::fromLocal8Bit(deviceCaps.szPname); #endif m_outputDevices << MIDIConnection(devName, dev); } } } void setPublicName(QString name) { if (m_publicName != name) { m_publicName = name; } } void open(const MIDIConnection& conn) { MMRESULT res; m_status = false; m_diagnostics.clear(); if (m_handle != nullptr) close(); reloadDeviceList(!m_clientFilter); if (!conn.first.isEmpty()) { res = midiOutOpen( &m_handle, conn.second.toInt(), (DWORD_PTR) midiCallback, (DWORD_PTR) this, CALLBACK_FUNCTION); if (res == MMSYSERR_NOERROR) { m_currentOutput = conn; m_status = true; } else { logError("midiStreamOpen()", res); } } } void close() { MMRESULT res; m_status = false; m_diagnostics.clear(); if (m_handle != nullptr) { res = midiOutReset( m_handle ); if (res != MMSYSERR_NOERROR) logError("midiOutReset()", res); res = midiOutClose( m_handle ); if (res == MMSYSERR_NOERROR) m_currentOutput = MIDIConnection(); else logError("midiStreamClose()", res); m_handle = nullptr; } } void doneHeader( LPMIDIHDR lpMidiHdr ) { MMRESULT res; res = midiOutUnprepareHeader( m_handle, lpMidiHdr, sizeof(MIDIHDR) ); if (res != MMSYSERR_NOERROR) logError("midiOutUnprepareHeader()", res); if ((lpMidiHdr->dwFlags & MHDR_ISSTRM) == 0) return; // sysex header? } void sendShortMessage(WinMIDIPacket &packet) { MMRESULT res; res = midiOutShortMsg( m_handle, packet.dwPacket ); if ( res != MMSYSERR_NOERROR ) logError("midiOutShortMsg()", res); } void sendSysexEvent(const QByteArray& data) { MMRESULT res; m_sysexBuffer = data; m_midiSysexHdr.lpData = (LPSTR) m_sysexBuffer.data(); m_midiSysexHdr.dwBufferLength = m_sysexBuffer.size(); m_midiSysexHdr.dwBytesRecorded = m_sysexBuffer.size(); m_midiSysexHdr.dwFlags = 0; m_midiSysexHdr.dwUser = 0; res = midiOutPrepareHeader( m_handle, &m_midiSysexHdr, sizeof(MIDIHDR) ); if (res != MMSYSERR_NOERROR) logError("midiOutPrepareHeader()", res); else { res = midiOutLongMsg( m_handle, &m_midiSysexHdr, sizeof(MIDIHDR) ); if (res != MMSYSERR_NOERROR) logError("midiOutLongMsg()", res); } } QString mmErrorString(MMRESULT err) { QString errstr; #ifdef UNICODE WCHAR buffer[1024]; midiOutGetErrorText(err, &buffer[0], sizeof(buffer)); errstr = QString::fromWCharArray(buffer); #else char buffer[1024]; midiOutGetErrorText(err, &buffer[0], sizeof(buffer)); errstr = QString::fromLocal8Bit(buffer); #endif return errstr; } void logError(const QString context, const MMRESULT code) { m_diagnostics << QString("%1 error: %2 (%3)").arg(context).arg(code).arg(mmErrorString(code)); } }; void CALLBACK midiCallback( HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { Q_UNUSED(hmo) Q_UNUSED(dwParam2) WinMIDIOutput::WinMIDIOutputPrivate* obj = (WinMIDIOutput::WinMIDIOutputPrivate*) dwInstance; switch( wMsg ) { case MOM_DONE: obj->doneHeader( (LPMIDIHDR) dwParam1 ); break; case MOM_OPEN: obj->m_diagnostics << "Open output"; break; case MOM_CLOSE: obj->m_diagnostics << "Close output"; break; default: obj->m_diagnostics << QString("unknown output message: %1").arg(wMsg); break; } } WinMIDIOutput::WinMIDIOutput(QObject *parent) : MIDIOutput(parent), d(new WinMIDIOutputPrivate) { } WinMIDIOutput::~WinMIDIOutput() { delete d; } void WinMIDIOutput::initialize(QSettings *settings) { Q_UNUSED(settings) } QString WinMIDIOutput::backendName() { return "Windows MM"; } QString WinMIDIOutput::publicName() { return d->m_publicName; } void WinMIDIOutput::setPublicName(QString name) { d->setPublicName(name); } QList WinMIDIOutput::connections(bool advanced) { d->reloadDeviceList(advanced); return d->m_outputDevices; } void WinMIDIOutput::setExcludedConnections(QStringList conns) { d->m_excludedNames = conns; } void WinMIDIOutput::open(const MIDIConnection& name) { d->open(name); } void WinMIDIOutput::close() { d->close(); } MIDIConnection WinMIDIOutput::currentConnection() { return d->m_currentOutput; } void WinMIDIOutput::sendNoteOn(int chan, int note, int vel) { WinMIDIPacket packet; packet.data[0] = MIDI_STATUS_NOTEON | (chan & MIDI_CHANNEL_MASK); packet.data[1] = note; packet.data[2] = vel; d->sendShortMessage(packet); } void WinMIDIOutput::sendNoteOff(int chan, int note, int vel) { WinMIDIPacket packet; packet.data[0] = MIDI_STATUS_NOTEOFF | (chan & MIDI_CHANNEL_MASK); packet.data[1] = note; packet.data[2] = vel; d->sendShortMessage(packet); } void WinMIDIOutput::sendController(int chan, int control, int value) { WinMIDIPacket packet; packet.data[0] = MIDI_STATUS_CONTROLCHANGE | (chan & MIDI_CHANNEL_MASK); packet.data[1] = control; packet.data[2] = value; d->sendShortMessage(packet); } void WinMIDIOutput::sendKeyPressure(int chan, int note, int value) { WinMIDIPacket packet; packet.data[0] = MIDI_STATUS_KEYPRESURE | (chan & MIDI_CHANNEL_MASK); packet.data[1] = note; packet.data[2] = value; d->sendShortMessage(packet); } void WinMIDIOutput::sendProgram(int chan, int program) { WinMIDIPacket packet; packet.data[0] = MIDI_STATUS_PROGRAMCHANGE | (chan & MIDI_CHANNEL_MASK); packet.data[1] = program; d->sendShortMessage(packet); } void WinMIDIOutput::sendChannelPressure(int chan, int value) { WinMIDIPacket packet; packet.data[0] = MIDI_STATUS_CHANNELPRESSURE | (chan & MIDI_CHANNEL_MASK); packet.data[1] = value; d->sendShortMessage(packet); } void WinMIDIOutput::sendPitchBend(int chan, int v) { WinMIDIPacket packet; // -8192 <= v <= 8191; 0 <= value <= 16384 int value = 8192 + v; packet.data[0] = MIDI_STATUS_PITCHBEND | (chan & MIDI_CHANNEL_MASK); packet.data[1] = MIDI_LSB(value); packet.data[2] = MIDI_MSB(value); d->sendShortMessage(packet); } void WinMIDIOutput::sendSystemMsg(const int status) { WinMIDIPacket packet; packet.data[0] = status; d->sendShortMessage(packet); } void WinMIDIOutput::sendSysex(const QByteArray &data) { d->sendSysexEvent(data); } QStringList WinMIDIOutput::getDiagnostics() { return d->m_diagnostics; } bool WinMIDIOutput::getStatus() { return d->m_status; } }} drumstick-2.9.0/library/rt-backends/win-out/winmidioutput.h0000644000175000017500000000502114541630232023066 0ustar pedropedro/* Drumstick RT Windows Backend Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef WINMIDIOUTPUT_H #define WINMIDIOUTPUT_H #include #include namespace drumstick { namespace rt { class WinMIDIOutput : public MIDIOutput { Q_OBJECT Q_PLUGIN_METADATA(IID "net.sourceforge.drumstick.rt.MIDIOutput/2.0") Q_INTERFACES(drumstick::rt::MIDIOutput) Q_PROPERTY(QStringList diagnostics READ getDiagnostics) Q_PROPERTY(bool status READ getStatus); public: class WinMIDIOutputPrivate; explicit WinMIDIOutput(QObject *parent = nullptr); virtual ~WinMIDIOutput(); // MIDIOutput interface virtual void initialize(QSettings* settings); virtual QString backendName(); virtual QString publicName(); virtual void setPublicName(QString name); virtual QList connections(bool advanced); virtual void setExcludedConnections(QStringList conns); virtual void open(const MIDIConnection& name); virtual void close(); virtual MIDIConnection currentConnection(); public Q_SLOTS: virtual void sendNoteOff(int chan, int note, int vel); virtual void sendNoteOn(int chan, int note, int vel); virtual void sendKeyPressure(int chan, int note, int value); virtual void sendController(int chan, int control, int value); virtual void sendProgram(int chan, int program); virtual void sendChannelPressure(int chan, int value); virtual void sendPitchBend(int chan, int value); virtual void sendSysex(const QByteArray &data); virtual void sendSystemMsg(const int status); private: WinMIDIOutputPrivate * const d; private: QStringList getDiagnostics(); bool getStatus(); }; }} #endif // WINMIDIOUTPUT_H drumstick-2.9.0/library/rt-backends/rt-backends.pro0000644000175000017500000000104114541630232021307 0ustar pedropedroTEMPLATE = subdirs #dummy { # SUBDIRS += dummy-in dummy-out #} linux { SUBDIRS += alsa-in alsa-out } unix:!macx { SUBDIRS += oss-in oss-out } macx { SUBDIRS += mac-in mac-out macsynth exists(/Library/Frameworks/FluidSynth.framework/Headers/*) { SUBDIRS += fluidsynth } } else { packagesExist(fluidsynth) { SUBDIRS += fluidsynth } packagesExist(libpulse-simple):packagesExist(sonivox) { SUBDIRS += eassynth } } win32 { SUBDIRS += win-in win-out } SUBDIRS += net-in net-out drumstick-2.9.0/library/rt-backends/CMakeLists.txt0000644000175000017500000000321114541630232021131 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(DRUMSTICK_PLUGINS_DIR "drumstick2") if(FALSE) add_subdirectory(dummy-in) add_subdirectory(dummy-out) endif() if(BUILD_ALSA AND ALSA_FOUND) add_subdirectory(alsa-in) add_subdirectory(alsa-out) endif() if (HAVE_PULSEAUDIO AND HAVE_SONIVOX) add_subdirectory(eassynth) endif() if(HAVE_NETWORK) add_subdirectory(net-in) add_subdirectory(net-out) endif() if(UNIX AND NOT APPLE AND NOT EMSCRIPTEN) add_subdirectory(oss-in) add_subdirectory(oss-out) endif() if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") add_subdirectory(mac-in) add_subdirectory(mac-out) add_subdirectory(macsynth) endif() if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") add_subdirectory(win-in) add_subdirectory(win-out) endif() if(HAVE_FLUIDSYNTH) add_subdirectory(fluidsynth) endif() drumstick-2.9.0/library/rt/0000755000175000017500000000000014541630232014624 5ustar pedropedrodrumstick-2.9.0/library/rt/backendmanager.cpp0000644000175000017500000002735014541630232020261 0ustar pedropedro/* Drumstick RT (realtime MIDI In/Out) Copyright (C) 2009-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include /** * @file backendmanager.cpp * Implementation of a class managing realtime MIDI input/output backends */ namespace drumstick { namespace rt { /** * @addtogroup RT * @{ * * BackendManager provides a mechanism to find and enumerate backends (plugins) * to manage realtime MIDI input/output * * This class and plugins are multiplatform. The backends implement one of these * interfaces: * * MIDIInput: for plugins that can receive MIDI events * * MIDIOutput: for plugins that can consume MIDI events * * @} */ class BackendManager::BackendManagerPrivate { public: QList m_inputsList; QList m_outputsList; QString m_inputBackend{QLatin1String("Network")}; #if defined(Q_OS_LINUX) QStringList m_outputBackends{QLatin1String("SonivoxEAS"),QLatin1String("FluidSynth"),QLatin1String("ALSA")}; #elif defined(Q_OS_DARWIN) QStringList m_outputBackends{QLatin1String("DLS Synth"),QLatin1String("FluidSynth"),QLatin1String("CoreMIDI")}; #elif defined(Q_OS_WINDOWS) QStringList m_outputBackends{QLatin1String("Windows MM"),QLatin1String("FluidSynth")}; #elif defined(Q_OS_UNIX) QStringList m_outputBackends{QLatin1String("FluidSynth"),QLatin1String("OSS")}; #else QStringList m_outputBackends{m_inputBackend}; #endif ~BackendManagerPrivate() { clearLists(); } void clearLists() { m_inputsList.clear(); m_outputsList.clear(); } void appendDir(const QString& candidate, QStringList& result) { QDir checked(candidate.trimmed()); //qDebug() << Q_FUNC_INFO << candidate << "exists:" << checked.exists(); if (checked.exists() && !result.contains(checked.absolutePath())) { result << checked.absolutePath(); } } }; /** * @brief Constructor */ BackendManager::BackendManager(): d(new BackendManagerPrivate) { QVariantMap defaultSettings { { QSTR_DRUMSTICKRT_PUBLICNAMEIN, QStringLiteral("MIDI In")}, { QSTR_DRUMSTICKRT_PUBLICNAMEOUT, QStringLiteral("MIDI Out")} }; refresh(defaultSettings); } /** * @brief Destructor */ BackendManager::~BackendManager() { } /** * @brief returns the paths where backends are searched * @return list of paths */ QStringList BackendManager::defaultPaths() { QStringList result; QString appPath = QCoreApplication::applicationDirPath() + QDir::separator(); #if defined(Q_OS_WIN) d->appendDir( appPath + QSTR_DRUMSTICK, result ); d->appendDir( appPath + "../lib/" + QSTR_DRUMSTICK, result ); #else #if defined(Q_OS_MAC) d->appendDir( appPath + QStringLiteral("../PlugIns/") + QSTR_DRUMSTICK, result ); #endif // Linux, Unix... QStringList libs; libs << "../lib/"; #if defined(LIBSUFFIX) QString libextra(QT_STRINGIFY(LIBSUFFIX)); if (QDir::isAbsolutePath(libextra)) { d->appendDir( libextra + QDir::separator() + QSTR_DRUMSTICK, result ); } else { libs << QString("../%1/").arg(libextra); } #endif foreach(const QString& lib, libs) { d->appendDir( appPath + lib + QSTR_DRUMSTICK, result ); } #endif d->appendDir( appPath + ".." + QDir::separator() + QSTR_DRUMSTICK, result ); QByteArray envdir = qgetenv(QSTR_DRUMSTICKRT.toLatin1()); //qDebug() << Q_FUNC_INFO << "envdir:" << envdir; if(!envdir.isEmpty()) { d->appendDir(QString(envdir), result ); } d->appendDir( QDir::homePath() + QDir::separator() + QSTR_DRUMSTICK, result ); #if QT_VERSION < QT_VERSION_CHECK(6,0,0) d->appendDir( QLibraryInfo::location(QLibraryInfo::PluginsPath) + QDir::separator() + QSTR_DRUMSTICK, result ); #else d->appendDir( QLibraryInfo::path(QLibraryInfo::PluginsPath) + QDir::separator() + QSTR_DRUMSTICK, result ); #endif foreach(const QString& path, QCoreApplication::libraryPaths()) { d->appendDir( path + QDir::separator() + QSTR_DRUMSTICK, result ); } return result; } /** * @brief BackendManager::refresh finds the installed backends applying the provided settings. * @param settings Program settings */ void BackendManager::refresh(QSettings *settings) { QVariantMap tmpMap; settings->beginGroup(QSTR_DRUMSTICKRT_GROUP); const QStringList allKeys = settings->allKeys(); //qDebug() << Q_FUNC_INFO << allKeys; for(const auto &k : allKeys) { tmpMap.insert(k, settings->value(k)); } settings->endGroup(); refresh(tmpMap); } /** * @brief BackendManager::refresh finds the installed backends searching the list of paths * provided by the function defaultPaths() applying the provided settings map as well. * @param map Program settings relevant section */ void BackendManager::refresh(const QVariantMap &map) { QString name_in; QString name_out; QStringList names; QStringList paths; d->appendDir(map.value(QSTR_DRUMSTICKRT_PATH).toString(), paths); name_in = map.value(QSTR_DRUMSTICKRT_PUBLICNAMEIN).toString(); name_out = map.value(QSTR_DRUMSTICKRT_PUBLICNAMEOUT).toString(); names << map.value(QSTR_DRUMSTICKRT_EXCLUDED).toStringList(); names << (name_in.isEmpty() ? QStringLiteral("MIDI In") : name_in); names << (name_out.isEmpty() ? QStringLiteral("MIDI Out") : name_out); paths << defaultPaths(); //qDebug() << Q_FUNC_INFO << "names:" << names; //qDebug() << Q_FUNC_INFO << "paths:" << paths; d->clearLists(); // Dynamic backends foreach(const QString& dir, paths) { QDir pluginsDir(dir); foreach (QString fileName, pluginsDir.entryList(QDir::Files)) { if (QLibrary::isLibrary(fileName)) { QPluginLoader loader(pluginsDir.absoluteFilePath(fileName)); QObject *obj = loader.instance(); if (obj != nullptr) { MIDIInput *input = qobject_cast(obj); if (input != nullptr && !d->m_inputsList.contains(input)) { if (!name_in.isEmpty()) { input->setPublicName(name_in); } input->setExcludedConnections(names); d->m_inputsList << input; } else { MIDIOutput *output = qobject_cast(obj); if (output != nullptr && !d->m_outputsList.contains(output)) { if (!name_out.isEmpty()) { output->setPublicName(name_out); } output->setExcludedConnections(names); d->m_outputsList << output; } } } } } } // Static backends foreach(QObject* obj, QPluginLoader::staticInstances()) { if (obj != nullptr) { MIDIInput *input = qobject_cast(obj); if (input != nullptr && !d->m_inputsList.contains(input)) { if (!name_in.isEmpty()) { input->setPublicName(name_in); } input->setExcludedConnections(names); d->m_inputsList << input; } else { MIDIOutput *output = qobject_cast(obj); if (output != nullptr && !d->m_outputsList.contains(output)) { if (!name_out.isEmpty()) { output->setPublicName(name_out); } output->setExcludedConnections(names); d->m_outputsList << output; } } } } } QList BackendManager::availableInputs() { return d->m_inputsList; } QList BackendManager::availableOutputs() { return d->m_outputsList; } MIDIInput* BackendManager::inputBackendByName(const QString name) { foreach (MIDIInput* i, d->m_inputsList) { if (i->backendName() == name) { return i; } } return nullptr; } MIDIOutput* BackendManager::outputBackendByName(const QString name) { foreach (MIDIOutput* i, d->m_outputsList) { if (i->backendName() == name) { return i; } } return nullptr; } MIDIInput* BackendManager::findInput(QString name) { QStringList names{name}; names << d->m_inputBackend; names.removeDuplicates(); if (!names.isEmpty()) { foreach(const QString& n, names) { foreach(MIDIInput* input, d->m_inputsList) { if (input->backendName() == n) { return input; } } } } return nullptr; } MIDIOutput* BackendManager::findOutput(QString name) { QStringList names{name}; names << d->m_outputBackends; names.removeDuplicates(); if (!names.isEmpty()) { foreach(const QString& n, names) { foreach(MIDIOutput* output, d->m_outputsList) { if (output->backendName() == n) { return output; } } } } return nullptr; } const QString BackendManager::QSTR_DRUMSTICK = QStringLiteral("drumstick2"); const QString BackendManager::QSTR_DRUMSTICK_VERSION = QStringLiteral(QT_STRINGIFY(VERSION)); const QString BackendManager::QSTR_DRUMSTICKRT = QStringLiteral("DRUMSTICKRT"); const QString BackendManager::QSTR_DRUMSTICKRT_GROUP = QStringLiteral("DrumstickRT"); const QString BackendManager::QSTR_DRUMSTICKRT_PUBLICNAMEIN = QStringLiteral("PublicNameIN"); const QString BackendManager::QSTR_DRUMSTICKRT_PUBLICNAMEOUT = QStringLiteral("PublicNameOUT"); const QString BackendManager::QSTR_DRUMSTICKRT_EXCLUDED = QStringLiteral("ExcludedNames"); const QString BackendManager::QSTR_DRUMSTICKRT_PATH = QStringLiteral("BackendsPath"); /** * @brief drumstickLibraryVersion provides the Drumstick version as an edited QString * @return Drumstick library version */ QString drumstickLibraryVersion() { return BackendManager::QSTR_DRUMSTICK_VERSION; } } // namespace rt } // namespace drumstick drumstick-2.9.0/library/rt/drumstick-rt-config.cmake0000644000175000017500000000014114541630232021515 0ustar pedropedro#include(CMakeFindDependencyMacro) include(${CMAKE_CURRENT_LIST_DIR}/drumstick-rt-targets.cmake) drumstick-2.9.0/library/rt/rt.pro0000644000175000017500000000203314541630232015771 0ustar pedropedroTEMPLATE = lib TARGET = drumstick-rt DESTDIR = ../../build/lib DEPENDPATH += . ../include INCLUDEPATH += . ../include include (../../global.pri) CONFIG += c++11 qt create_pc create_prl no_install_prl static { CONFIG += staticlib } DEFINES += drumstick_rt_EXPORTS QMAKE_CXXFLAGS += $$QMAKE_CXXFLAGS_HIDESYMS QMAKE_PKGCONFIG_PREFIX = $$INSTALLBASE QT -= gui # Input HEADERS += \ ../include/drumstick/rtmidiinput.h \ ../include/drumstick/rtmidioutput.h \ ../include/drumstick/backendmanager.h \ ../include/drumstick/macros.h SOURCES += \ backendmanager.cpp macx:!static { TARGET = drumstick-rt CONFIG += lib_bundle FRAMEWORK_HEADERS.version = Versions FRAMEWORK_HEADERS.files = $$HEADERS FRAMEWORK_HEADERS.path = Headers/drumstick QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS #QMAKE_LFLAGS_SONAME = -Wl,-install_name,@executable_path/../Frameworks/ QMAKE_SONAME_PREFIX = @rpath QMAKE_TARGET_BUNDLE_PREFIX = net.sourceforge QMAKE_BUNDLE = drumstick-rt QMAKE_INFO_PLIST = ../Info.plist.lib } drumstick-2.9.0/library/rt/CMakeLists.txt0000644000175000017500000001034614541630232017370 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(CMAKE_INCLUDE_CURRENT_DIR ON) set(drumstick-rt_QOBJ_SRCS ../include/drumstick/rtmidiinput.h ../include/drumstick/rtmidioutput.h ) set(drumstick-rt_HEADERS ../include/drumstick/macros.h ../include/drumstick/rtmidiinput.h ../include/drumstick/rtmidioutput.h ../include/drumstick/backendmanager.h ) if(BUILD_FRAMEWORKS) set_source_files_properties(${drumstick-rt_HEADERS} PROPERTIES MACOSX_PACKAGE_LOCATION Headers/drumstick ) endif() set(drumstick-rt_SRCS backendmanager.cpp ) if (WIN32) set(TARGET_DESCRIPTION ${Drumstick_DESCRIPTION}) set(TARGET_NAME drumstick-rt) set(TARGET_ORIGINAL_FILENAME libdrumstick-rt.dll) configure_file(${Drumstick_SOURCE_DIR}/versioninfo.rc.in versioninfo.rc @ONLY) list(APPEND drumstick-rt_SRCS versioninfo.rc) endif() if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(drumstick-rt_MOC_SRCS ${drumstick-rt_QOBJ_SRCS}) else() qt_wrap_cpp(drumstick-rt_MOC_SRCS ${drumstick-rt_QOBJ_SRCS}) endif() add_library(drumstick-rt ${drumstick-rt_MOC_SRCS} ${drumstick-rt_SRCS} ${drumstick-rt_HEADERS} ) add_library(Drumstick::RT ALIAS drumstick-rt) target_include_directories(drumstick-rt PUBLIC $ $ ) target_compile_definitions(drumstick-rt PRIVATE LIBSUFFIX=${CMAKE_INSTALL_LIBDIR} QT_NO_SIGNALS_SLOTS_KEYWORDS ) target_link_libraries(drumstick-rt PRIVATE Qt${QT_VERSION_MAJOR}::Core ) if(STATIC_DRUMSTICK) set_target_properties(drumstick-rt PROPERTIES STATIC_LIB "libdrumstick-rt" EXPORT_NAME RT ) else() # STATIC_DRUMSTICK set_target_properties(drumstick-rt PROPERTIES VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH} SOVERSION ${PROJECT_VERSION_MAJOR} EXPORT_NAME RT # macOS: MACOSX_RPATH TRUE ) if (BUILD_FRAMEWORKS) set_target_properties(drumstick-rt PROPERTIES FRAMEWORK ${BUILD_FRAMEWORKS} FRAMEWORK_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH} MACOSX_FRAMEWORK_IDENTIFIER "net.sourceforge.drumstick-rt" MACOSX_FRAMEWORK_INFO_PLIST "${CMAKE_SOURCE_DIR}/cmake_admin/CustomFrameworkInfo.plist.in" ) endif() endif() # STATIC_DRUMSTICK install(TARGETS drumstick-rt EXPORT drumstick-rt-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} ) install(FILES ${drumstick-rt_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/drumstick) install(EXPORT drumstick-rt-targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/drumstick NAMESPACE Drumstick:: ) export(EXPORT drumstick-rt-targets NAMESPACE Drumstick:: FILE ${CMAKE_BINARY_DIR}/drumstick-rt-targets.cmake ) include(CMakePackageConfigHelpers) write_basic_package_version_file(${CMAKE_BINARY_DIR}/drumstick-rt-config-version.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY AnyNewerVersion ) configure_file( drumstick-rt-config.cmake ${CMAKE_BINARY_DIR}/drumstick-rt-config.cmake @ONLY ) install(FILES ${CMAKE_BINARY_DIR}/drumstick-rt-config-version.cmake ${CMAKE_BINARY_DIR}/drumstick-rt-config.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/drumstick ) drumstick-2.9.0/library/vpiano-plugin/0000755000175000017500000000000014541630232016767 5ustar pedropedrodrumstick-2.9.0/library/vpiano-plugin/vpiano-plugin.cpp0000644000175000017500000000475114541630232022272 0ustar pedropedro/* Virtual Piano Widget for Qt Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include "vpiano-plugin.h" #include #include using namespace drumstick::widgets; /** * @file vpiano-plugin.cpp * Implementation of the PianoKeybdPlugin class (Qt Designer plugin) */ PianoKeybdPlugin::PianoKeybdPlugin(QObject *parent) : QObject(parent) { } void PianoKeybdPlugin::initialize(QDesignerFormEditorInterface * /* core */) { if (initialized) return; initialized = true; } bool PianoKeybdPlugin::isInitialized() const { return initialized; } QWidget *PianoKeybdPlugin::createWidget(QWidget *parent) { return new PianoKeybd(parent); } QString PianoKeybdPlugin::name() const { return QStringLiteral("drumstick::widgets::PianoKeybd"); } QString PianoKeybdPlugin::group() const { return QStringLiteral("Drumstick"); } QIcon PianoKeybdPlugin::icon() const { return QIcon(":/vpiano-plugin.png"); } QString PianoKeybdPlugin::toolTip() const { return QStringLiteral("Virtual Piano Keyboard"); } QString PianoKeybdPlugin::whatsThis() const { return QStringLiteral("The Virtual Piano Keyboard is a MIDI controller emulator"); } bool PianoKeybdPlugin::isContainer() const { return false; } QString PianoKeybdPlugin::includeFile() const { return QStringLiteral(""); } QString PianoKeybdPlugin::domXml() const { return "\n" " \n" " \n" " \n" " 0\n" " 0\n" " 640\n" " 80\n" " \n" " \n" " \n" ""; } drumstick-2.9.0/library/vpiano-plugin/vpiano-plugin.h0000644000175000017500000000374114541630232021735 0ustar pedropedro/* Virtual Piano Widget for Qt Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #ifndef VPIANOPLUGIN_H #define VPIANOPLUGIN_H #include /** * @file vpiano-plugin.h * PianoKeybdPlugin class definition (Qt Designer plugin) */ /** * @class QDesignerCustomWidgetInterface * @brief The QDesignerCustomWidgetInterface class enables Qt Designer to access and construct custom widgets. * @see https://doc.qt.io/qt-5/qdesignercustomwidgetinterface.html */ class PianoKeybdPlugin : public QObject, public QDesignerCustomWidgetInterface { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface") Q_INTERFACES(QDesignerCustomWidgetInterface) public: explicit PianoKeybdPlugin(QObject *parent = nullptr); bool isContainer() const override; bool isInitialized() const override; QIcon icon() const override; QString domXml() const override; QString group() const override; QString includeFile() const override; QString name() const override; QString toolTip() const override; QString whatsThis() const override; QWidget *createWidget(QWidget *parent) override; void initialize(QDesignerFormEditorInterface *core) override; private: bool initialized = false; }; #endif // VPIANOPLUGIN_H drumstick-2.9.0/library/vpiano-plugin/vpiano-plugin.png0000644000175000017500000000111114541630232022257 0ustar pedropedroPNG  IHDR szzbKGD pHYs  tIME 8+93tEXtCommentCreated with The GIMPd%nIDATX헿@2BRDO#X +;V,|}_<QDD3[|!d90Ŝ{70Ey\.T^v|>Ǚal6h4G)yt:>. RȄ\וRZJJ< F1&'Iʛ繵J !UQ1~S{4AIyZ! 4@hmۙ!m4LnYַj\\.4zNrRl¶mZ-mPJ>APp8`0`! ø/!~?Cl6SEr<2N~@h%RYb~R"5#~=|*k+8c^˼q4MDQʌ`,ʌlޜsMG ULIENDB`drumstick-2.9.0/library/vpiano-plugin/vpiano-plugin.pro0000644000175000017500000000111714541630232022301 0ustar pedropedroTEMPLATE = lib TARGET = drumstick-vpiano-plugin target.path = $$[QT_INSTALL_PLUGINS]/designer INSTALLS += target DESTDIR = ../../build/lib/designer DEPENDPATH += . ../include INCLUDEPATH += . ../include include (../../global.pri) QT += widgets uiplugin CONFIG += plugin HEADERS += vpiano-plugin.h SOURCES += vpiano-plugin.cpp RESOURCES += vpiano-plugin.qrc macx:!static { QMAKE_LFLAGS += -F$$OUT_PWD/../../build/lib -L$$OUT_PWD/../../build/lib LIBS += -framework drumstick-widgets } else { LIBS += -L$$OUT_PWD/../../build/lib \ -l$$drumstickLib(drumstick-widgets) } drumstick-2.9.0/library/vpiano-plugin/vpiano-plugin.qrc0000644000175000017500000000014014541630232022261 0ustar pedropedro vpiano-plugin.png drumstick-2.9.0/library/vpiano-plugin/CMakeLists.txt0000644000175000017500000000441214541630232021530 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) find_package(Qt${QT_VERSION_MAJOR}Widgets REQUIRED) find_package(Qt${QT_VERSION_MAJOR}UiPlugin REQUIRED) set(SOURCES vpiano-plugin.h vpiano-plugin.cpp vpiano-plugin.qrc) add_library(drumstick-vpiano-plugin MODULE ${SOURCES}) target_compile_definitions(drumstick-vpiano-plugin PRIVATE QT_PLUGIN) target_include_directories(drumstick-vpiano-plugin PRIVATE ${Drumstick_SOURCE_DIR}/library/include ) target_link_libraries(drumstick-vpiano-plugin PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::UiPlugin Drumstick::Widgets ) set_target_properties(drumstick-vpiano-plugin PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib/designer) get_target_property(QT_QMAKE_EXECUTABLE Qt${QT_VERSION_MAJOR}::qmake LOCATION) execute_process(COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_INSTALL_PLUGINS OUTPUT_VARIABLE QT_INSTALL_PLUGINS OUTPUT_STRIP_TRAILING_WHITESPACE ) execute_process(COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_HOST_PREFIX OUTPUT_VARIABLE QT_HOST_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE ) string(REPLACE "${QT_HOST_PREFIX}/" "" _INSTALL_PLUGINS "${QT_INSTALL_PLUGINS}") install(TARGETS drumstick-vpiano-plugin EXPORT drumstick-vpiano-targets RUNTIME DESTINATION ${_INSTALL_PLUGINS}/designer ARCHIVE DESTINATION ${_INSTALL_PLUGINS}/designer LIBRARY DESTINATION ${_INSTALL_PLUGINS}/designer) drumstick-2.9.0/library/widgets/0000755000175000017500000000000014541630232015645 5ustar pedropedrodrumstick-2.9.0/library/widgets/blkey.png0000644000175000017500000005301014541630232017460 0ustar pedropedroPNG  IHDR97ZWbKGD pHYs.#.#x?vtIME  0M IDATxߓ,QϩV+V])d!d͂%"#?'" o/<͏" @ @.C 3]U~PٜsTS՝qTWUde3s26l6l6l`7_[?u]߶m4Mvv֯Vm[Vvfq]u~kl6f黮k޽3~}:x}w]皦r^u۝{E绾?裏s>3 svعG;>]uzڶjo]4]۶Ψ/_Vsykιιsߩ٘c蜻cGGO]o=| χz/=_>{8Mrc~;r9g6gV~m[Taϋc79ǧ^z\ع% W50_:iDm5b^s9yCdpcSĞks]P^y^'rtt+x`Q{+9_z<UB9=4MCS +,F ]XyLcŚTE<ĸ먐UcUCz%}WX2$sF/O;;!>BiHykgCOV<SI)t$HWٯ.m7:ZjRxR>u`Ht$ 1JiwI5$T( Icrq/)ZAK S#F::supn%G7M1H6lbKBGR=V7bA V`8u5 +ݛSa^'g21=i;l3s+H5JX_svnƭ*A24XmXX?G*hQj q-2̘hT7QNE+tp7~WRwC24#7!s'M~< wy`=#ItsG ͏ylJ}#ho fЧ)yC+qXǁIQz^j2RҰԘ %kf./H ke.㑋.)[II Cr #u$œQNq搠nVޗ3!i]Gbs 's) qcpf! iQWO:yy܂JT1HczDzq~ؒ@GR)_qv$IeBGn2tTS7R1BK,cs){n Hz](uH͇!t (w* s,FU7if:D)}"ˁAF HGz\"#M0t4"bA 8)2&Q]VL[nB\jss(MK@GqNc. 5UUKstj@H MNAG;J+wT QPX}n*=ve+Bst1W\SBGTvN曱W.rM"RơE1Q긹ySbv8ʺ$7&Ɂř'}v"/p Ts3G\z* cyiɜB]% БT7)UI\# oܸ1[dL%q.hntYmP%CVKYZBK~ S3Xt3KԬXe0JD9$ɐCGcNXH|쇆 Z.ls)K;քͥe7۬1f|#CSb6;0R+l,qC0 JFA߼ysWf!F5ExP=LD7Szcudca?]ym,4B)3$,+XЍ m,^(W7y\#utsLoeQ&F:e!Zx,tʗOoRQJo/}K s(FrgK,Kͅ(i}4( n*uKs#Ϫ”28M5AG46tD-e {yH7RK֏ݬ+(J};99\K\: NJ<$K\n,q}CA> Sw0RK:5F}Kb9>:J=NCGRs0ҰbO!41cW9xH9y!H<%. 0*T~:#Į^P7)# Ka6XGLX?լX\,a9~s6Y *l!!wN[p8͙΁Do\;\++.7~x*+ san1#m,4B)(&gx=5f6RCJ KQvs*9. Xi:b^Oej.s}{ K\֗BHfTAElH0!A2/p1fMs0Rֻ~:m,qĵ#WWYJ)Cy,qm(dz{uqbmmc [ق+(H)Ks%!tn&+TtjJl?iKoBfӓ20R=::\Ynvi89u}IC1R+}csɅ"1-/:fԣ`u,1xK!TcS :f}m )rTy8:Ch)(CP1Fv# 99q]Wr)}M`uckAGյ1e14r8bt9:tTz,qObВ+1 =# :*Eqr44`CtX/:)o*XD5<4|HQI+K@m kqɘq.0R R* Xb.!TKt3<^M0G+S+%;K\w,xfu{obTf*}oc\:Hnv37a54ƆRrCZzn* AsjuS% IWS)4t CGZׄY׬iz(Fjc {kbվVNH%^(tSݠ>*)HCI w%?Nr>6Zu19%;8'a 6)j4>r%ChL S b4fQĚtsn;П.r_6xzXLͽ:$FӀ6xU#n.v0#n ,BБĵVSX999! k.H5jc+% Ij$CV\*Xy:)Ĥu#7/-/=vׄbYjh #ŖFjcE^|V(3[aXvݧĥ#.+Q7K0X&׆8݌W`6XCSϫ H\O /.prD[qe0R앩JJ |QnhDlk(Jc1RX6hZXCu3ҧKcoct3|^ $c0R7C聍%v#iqE>b6X!Tt;>&[u3X44dXc'DϫAvJd3m,rTqEUƓhFZ F;WC2tN ]#]XS@GZt3%ϸ: KcC*Z=:F7sPpcxMБUu3~^]_Ny=XX#YZ<%c0RJB2e!4tEyXkFvMXБ[gAGc%ҷDC`8x::8T94F%T#kK,Sts)X_U. $C0R% Б%gv-ߠ۶`?n!X\p_  ThK)  IЇK IC44=f)t8^MP8-TRwSE\_F.HqPv]2t`ʘM6( yF,e,!ttsV/IlFJcSz:ru+ACK0R?#Iu!AG)-͔qRŎg^^Cb R :r|G+v-pϏ7ڠ%0TB7SE}KӻR_/&(<>>vPNI0R +8wckt́Y]Fm:%Chta_%)UPT E::8?X >kyj:KwG #Eks%v`iĥa}#9OA_) ot4X~ :^^zBVCK54':'t%'VrPw(Fg}KQɱĚ:ր$04kh1i@ m,O5C.4F26x(7[4!s_6IUj%76t$* "!C0Zu, :*9trl_Zim,qݱc1Yu_h%0R(7 :7x.ݬ.(ܥS ,%^tȠ1@G%t3cF&5q #M_0ҡ٠a7:$ )x@#l,r#θEm7f)gh %4X-Rĥu9QN? D)!%p2XbCXݜJC16xzX?'( VÔTs0R5C!%ƙܱĹœ1AcKa+:Fp͹3GEP euCh)(j7(H!!^֢,61)U5\*tm[4ţ,G+bSJᾌ%΅9z9ej{2i4<:P) 0<]SX#ōfCBG8T;֍ZZ#tySF2ܯ+K%& e,q$EAG1nVnF=%7EnV}iOe6r0RNv@ocuCG!M BNsiJC-%K<=tqϫb9$^9?&U8t1̹ꡣnJ[H饠Zc*RckBGnVH-\i<#U!tU7QrusxTyea.G!t>(>JfdpLz0R4XP#*u3tݓD7)(d6Rʎ :;v$%5fFJykZzQNfenV;-J&HQXeRݬn[u^;gi=L6- ֧gٶze.*]J9 m,|Bsu3Ԋq[85ϡe,qm(VlQ%9HSXⱰĚus-(e*a,eC0Re9JCGRXX+kAGR<:59)DJ+ 1<:J[Tih`>VTanFjcuIKᡡ\SCG1ݜq**`?df#%#cnG(u6(U }Ha?0FK,; tTK78,H" 9X[ҺYeJʐbب5CGKO( oMdx5 HK8-KzhБsk\9KuG;ITU ?#]:ӽqǧV{9xļuLC[eCGnV9T0RXihؙ6t(cn5dq@vxF oQnEn. Io:_ #\ ^:nYw-qy, ] L)(m#.T)9?a`V(#2tfuc݈F3!Hᘂ%%+ŗ)aŷ(J$K\ChnsI gm,u3<|~/墇`t`q;f)x ݜ2fUpRʹ)`:^Ew&|WNazKI]ZC֗d7TSHap޷AGwMa$T[FD?#]jХ% ŌY>FzF2#cB4xn "U⣫*ꄣ8oKisIX_#t]/z 9) C_XPӒsиbd9 !#Kѹ*BN.4dQj%!%$t:|&*(o] #mƠ#W,q Hr Sn L-:ziuo@B~ XXAG : Ma4ewaG5d`׶ $c)|AG˂4{&q\xFJ -ػKAORyt$5r5o+B/t$8Tf)c@_2L41mcut`1AIHr^You]0t$" +a1YCGvs܏DHa6X✴\Iݬ6v+XbT:ZFFFJċ)<5po.tlHRdQRe%!npRaУXl,958Bx+kXK==ߠ!-HMyhͪjM QhcSCK\)¨ rf%iL?sO͵')ݬ2Hs$%:e$,G*1#C/U7kK\֗fuYAu4Wb9nK\y,T7sƙ*PPCJZg,u̜Z  (F}TK02D`:e90RJFb野J uAapTz,F yh œ}K\:Y>Gf<i,%^dѪ1kGT^Y:'q:n6h8tDU b%9MFHK\@7k ۶%{q&R)Ӫm,T3rU,GHݕHqdcCPۮ4F Ǻ )XBBG *AG}9fX$6XG\/*} lx)q}KCh{gMP;jEw #Zn^;V;t֗湍``XFbZ`A`ͥA7RƬf[ H9cC>%jIN`"MaNH,PyntAw]Gf6Ja!ZsuAG9Az8 .^/0Xu*|tZ{4~cUCͥ'%u3^$2#ծ%С#t}>5{b6h9Бvȟ`;.=#Mʥv=4(uKb.y,1:tfc&Y0#Eں!CG1 DgggB+NJ-.csq4upAa#$7T o&6 tI Uyh $?<74h'KSj28#u-9,b9b:0{ >-2}eXbl@sBGszhb2x<4ctĵP;[ 5DjБ?x vX]*MA속%^tͪTN2.X:.U7|3Xp 1FAGa\vx=&HKcKK#T+ļu #::5xLGХ%5[4 I˥0RwMaQ\7tT)mGy_ #w0m2#y'''r@+Cw62P :O݌RH`ԵbœںР#|ŠWե̆#eVDNZx,5ig}VGj^ {)oHq;]K,,^z%5A%y_W#_yK7 19uAGu{xMs(!H\BK%u]z~cI =A!b9yhJ9uB(r)~LHUNe1Rѐa?76xA UWRDJ !Xu.yhl%0%B5OEi;KaAG#nb3bYxSr$#%=b^TXС#ARH a:s%@i;jEX4VK%%.F i,šœCK3F7tdƜvFyg#%3t4X߂fFfg90d`\k塣!Z8+~eБlyhS<4sm,dyh)FJ%m<4:wtLzHЦ;C{.ہr #A ͘Q!;E6Kl\MCCoMxK0hecm1FSl 2f[Hm,T#@J n,EXb3 4áAPj͘'!W2f}M` 6r$bmU5h:n֠0UXbg!w[u}뻮뻮s=v8p~pᚻvq<\߶Nx=\8\Åmɽywhsv]s9\۶}ߟi5Mӷm뚦۶uN{?KK_}U z9!eݿy/o{748lX >=uAnx{ kn(st3u]w$vűo7/Wg1hewJnA~K *s?o_Få2\ΠлQC/(>3Rx-yW ݩ{7;$qZ>{ Y 1#:d;ΑcCĸr}=ι?@1kO.+sI5UBJCŊHu0U}TzKsq;H;hT?%t>1ᵐ#Sb0u{/o{П|,Cze&[wϑF?z$^J+;d%%p#\x㍟޿˲{)1.(2;.zBTZ"1fcr,'n[sIBH̥;b¾o-1b_&1tH sL['<40R@[[ɍ$FAeKKۏ L*9%E}OHw)]}$HKF{~W~!פ)c&RqI/k߽{tIbP^<5z  d4-H O{7ǗwN8nhFι들ގn?衇3y0͆c ,'':?6Zȭ3K>.<1Jyv]urRjιnWٻ_{u{~;21#]V7͛m~5!KbPӧǓih @⨼o)N͖$nt]ׯV:F㸺g)^yԿ#17Xblv}ݬGeR,S˰sR^xSIbPv=99s][U5蘆i=p% /pĸ>? ]:z4(6|6qUbqv JʎdCv] d.CKb5M%&1??Jb@gu{'Ҳc-и̜`OA`|ws=^7KbY =tTC{_|׿>bY ᳀:{0h}["}7HCKOK޾Lzi'M/gĸj%5RUC)Wx'j [Pk ā!$/npn y y}Kk5%&=4Xz8^~{sJ}Z%?;;{su`УwkQGD!޽{_<(APpv}9G檆QS&1Gbc$}F)ٱa H~ˠrݻ?By-bĀξs7'Hx3>+@n|b$AM5-j提$Iv{)}Fu݅~sLb$F)oֿ+ϝsתVw^z/1n83?#G4p{w'L3FCQq {P-8uﻶm;J`=MGkM\Ќ ǟg5~~ꩧt=PMbK wν94}5fv@\'Mb(1\eX0{[`O01Dbrw~3ꛏ?, ƽm~2\$޿U i-Lb(1s>T5,r z 7~2},BJ CCw=cGO?{I߭s4swMWՓW_}S{ĥJ 9e$FxnnW`q7nxslG_i01Vb@n}4w7'!cc^$JPGk'3KNY>Kh?z ~!Qk4]KS ѱc=vHmJ{&1J *;u{ȎbY@ mLb&,%1swQCoawm Xc,4f1Xn3ܖg7s'v}$,&1(^r㤄A[ .Egò#%F7ͻsdGYnvkLbL(1(\PD*1ݬ~لo~k_ڙ$}'1Z~_(ŀ}HY ʨw^z}9w{_ۘJ P? X{%ubPz2[ι׀ݘG@P$FA{Ÿ"1׸㏯/mS;lЯSz'7nmKdK J 0Š/ww7sowkB/x?ʲ&1&Иmۛ`!L1ƃȍy]˒F{ߟ\wL8_Q:zWLc%FZ%6SC] Pc {r_۵%8wa;Y$T#xi@pABMsלsOz޹xI 5%F8ƒ߸qxU8)'H}۶"{v]sݒ,v,F'_=dw-!E1ѵm{4Ѐ)r8intͦ ߹s?'&꡽O}Wս!]gY SI۳km(*k[V?}0QSb>Zpۢҷ@F-aYY hi۷oN,Gu/s²&1jKݻ!ݎ힅AI K c{!tu۶}%72bĠS"1(ϕ1;ΰ9AHlm-1Ġ2R7qΝKx饗^ù\ݟǣ h$IQwwc,9~w~移}{ lA,1V$'>/Z<40,Asx9  Db`:gַ`vιZ׏m]mCXum[4ͅ}d`yQσ| |xz=|۶ X=>h image/svg+xml drumstick-2.9.0/library/widgets/checked.png0000644000175000017500000000264214541630232017745 0ustar pedropedroPNG  IHDR$xgAMA a cHRMz&u0`:pQ<bKGDC pHYs]OtIME .r8qIDATHŗmLWyP'3Ρ"MZ 2%]kE'$c.qbaTh?Ta~,t0 /ڂՀ\dh%OϽܜs۹~*ŠW4FFzKrJ A߂$)a ge2OZ)CCGjqdW qR̓ˍOrP*+ϻhm:zwQpx^rE4CGE πFKcq037pC/J@>3sŀKi+&5^R i496$b4!;6.,iyҸd2e߭?ǙclRںLsqF/* ~/gGCMFZmGHO`ÇY7(SD\nK ƈ@Ad8wN qXffy/%x3~>bF& TU(TgJHҍHȿ8N;_bd7၁Bbő'z75UNL\P^LO. @m))ůw:(Z?R)%$"prv@\\HȺukDރz~C dbhSӶmǏ?;~X{1AGJ"Grs1H+l99N2žss% @* *|V}XIiXbonuxB®]Kgm&;S\4+Q w*bp^ deEGsxK覦O=7jn0옙:PkA׎_Ue^Ӄ߼_7yfިɑVs ݤ$傯6(rNS\U5m0 x<ljo笴 aaׯ WO,q91q_[S幛_iaBt_NC&W7[϶oG%䴴niffs 8 ɓQOs4+%}b8T+\n5H%tEXtdate:create2021-05-18T17:19:14+02:00$%tEXtdate:modify2018-04-07T09:46:27+02:00/̱IENDB`drumstick-2.9.0/library/widgets/checked.svg0000644000175000017500000000111714541630232017754 0ustar pedropedro drumstick-2.9.0/library/widgets/drumstick-widgets-config.cmake0000644000175000017500000000014614541630232023564 0ustar pedropedro#include(CMakeFindDependencyMacro) include(${CMAKE_CURRENT_LIST_DIR}/drumstick-widgets-targets.cmake) drumstick-2.9.0/library/widgets/error.png0000644000175000017500000000166514541630232017514 0ustar pedropedroPNG  IHDR$xgAMA a cHRMz&u0`:pQ<bKGDC pHYs]OtIME .r8qIDATHŗ1H[Aƿ$&1bA4YZ]BZ4K]JR9 cju-ء[AhPK&&:55q/֖{gl='; wU{q Nww_6IH=ae5P{02"[Oj4CYCe֛@WW$X,N@uxJeG08= Pj̀>779Y;xU"}ff| d*?'h4}vvb0T@oqPngϧiQ../3 Z\\]*_t8AOܙrGjP^"pv 9*ˠi==@B!:?rc1 )Z].ON dhow4Lf?;Eq{t1uKel]2,/ᡌ?#!*.LKKQwtaaeET7p<bP5܈"wuWjH2,Gyxm9 7GfRݜWV^T7Q}4AyxחoVLmidh${ZxOeE_^k)H)3Ʉ [-Ƈ/ww9lu?MPG%tEXtdate:create2021-05-18T17:19:23+02:00ը%tEXtdate:modify2018-04-07T09:46:27+02:00/̱IENDB`drumstick-2.9.0/library/widgets/error.svg0000644000175000017500000000122514541630232017517 0ustar pedropedro drumstick-2.9.0/library/widgets/fluidsettingsdialog.cpp0000644000175000017500000003541014541630232022420 0ustar pedropedro/* Drumstick MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "fluidsettingsdialog.h" #include "ui_fluidsettingsdialog.h" #include #include /** * @file fluidsettingsdialog.cpp * Implementation of the Fluidsynth configuration dialog */ namespace drumstick { namespace widgets { const QString FluidSettingsDialog::QSTR_PREFERENCES = QStringLiteral("FluidSynth"); const QString FluidSettingsDialog::QSTR_INSTRUMENTSDEFINITION = QStringLiteral("InstrumentsDefinition"); const QString FluidSettingsDialog::QSTR_DATADIR = QStringLiteral("soundfonts"); const QString FluidSettingsDialog::QSTR_DATADIR2 = QStringLiteral("sounds/sf2"); const QString FluidSettingsDialog::QSTR_AUDIODRIVER = QStringLiteral("AudioDriver"); const QString FluidSettingsDialog::QSTR_PERIODSIZE = QStringLiteral("PeriodSize"); const QString FluidSettingsDialog::QSTR_PERIODS = QStringLiteral("Periods"); const QString FluidSettingsDialog::QSTR_SAMPLERATE = QStringLiteral("SampleRate"); const QString FluidSettingsDialog::QSTR_CHORUS = QStringLiteral("Chorus"); const QString FluidSettingsDialog::QSTR_REVERB = QStringLiteral("Reverb"); const QString FluidSettingsDialog::QSTR_GAIN = QStringLiteral("Gain"); const QString FluidSettingsDialog::QSTR_POLYPHONY = QStringLiteral("Polyphony"); const QString FluidSettingsDialog::QSTR_BUFFERTIME = QStringLiteral("BufferTime"); const QString FluidSettingsDialog::QSTR_PULSEAUDIO = QStringLiteral("pulseaudio"); FluidSettingsDialog::FluidSettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::FluidSettingsDialog) { ui->setupUi(this); connect(ui->audioDriver, &QComboBox::currentTextChanged, this, &FluidSettingsDialog::audioDriverChanged); connect(ui->bufferTime, QOverload::of(&QSpinBox::valueChanged), this, &FluidSettingsDialog::bufferTimeChanged); connect(ui->periodSize, QOverload::of(&QSpinBox::valueChanged), this, &FluidSettingsDialog::bufferSizeChanged); connect(ui->periods, QOverload::of(&QSpinBox::valueChanged), this, &FluidSettingsDialog::bufferSizeChanged); connect(ui->btnFile, &QToolButton::clicked, this, &FluidSettingsDialog::showFileDialog); connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, &FluidSettingsDialog::restoreDefaults); auto sampleRateValidator = new QDoubleValidator(8000.0, 96000.0, 1, this); sampleRateValidator->setNotation(QDoubleValidator::StandardNotation); sampleRateValidator->setLocale(QLocale::c()); ui->sampleRate->setValidator(sampleRateValidator); auto gainValidator = new QDoubleValidator(0.1, 10.0, 2, this); gainValidator->setNotation(QDoubleValidator::StandardNotation); gainValidator->setLocale(QLocale::c()); ui->gain->setValidator(gainValidator); auto polyphonyValidator = new QIntValidator(1, 65535, this); ui->polyphony->setValidator(polyphonyValidator); drumstick::rt::BackendManager man; m_driver = man.outputBackendByName("FluidSynth"); if (m_driver != nullptr) { QVariant v = m_driver->property("audiodrivers"); if (v.isValid()) { ui->audioDriver->blockSignals(true); ui->audioDriver->clear(); ui->audioDriver->addItems(v.toStringList()); ui->audioDriver->blockSignals(false); } v = m_driver->property("soundfont"); if (v.isValid()) { m_defSoundFont = v.toString(); } } ui->bufferTime->blockSignals(true); ui->periodSize->blockSignals(true); ui->periods->blockSignals(true); //qDebug() << Q_FUNC_INFO; } FluidSettingsDialog::~FluidSettingsDialog() { //qDebug() << Q_FUNC_INFO; if (m_driver != nullptr) { m_driver->close(); } delete ui; } bool FluidSettingsDialog::checkRanges() const { //qDebug() << Q_FUNC_INFO; if (ui->gain->hasAcceptableInput()) { ui->gain->deselect(); } else { ui->gain->selectAll(); } if (ui->polyphony->hasAcceptableInput()) { ui->polyphony->deselect(); } else { ui->polyphony->selectAll(); } if (ui->sampleRate->hasAcceptableInput()) { ui->sampleRate->deselect(); } else { ui->sampleRate->selectAll(); } return ui->bufferTime->hasAcceptableInput() && ui->periodSize->hasAcceptableInput() && ui->periods->hasAcceptableInput() && ui->gain->hasAcceptableInput() && ui->polyphony->hasAcceptableInput() && ui->sampleRate->hasAcceptableInput(); } void FluidSettingsDialog::accept() { //qDebug() << Q_FUNC_INFO; if (checkRanges()) { writeSettings(); if (m_driver != nullptr) { QString title; QVariant varStatus = m_driver->property("status"); if (varStatus.isValid()) { title = varStatus.toBool() ? tr("FluidSynth Initialized") : tr("FluidSynth Initialization Failed"); QVariant varDiag = m_driver->property("diagnostics"); if (varDiag.isValid()) { QString text = varDiag.toStringList().join(QChar::LineFeed).trimmed(); if (varStatus.toBool()) { if (!text.isEmpty()) { QMessageBox::information(this, title, text); } } else { QMessageBox::critical(this, title, text); return; } } } } QDialog::accept(); } } void FluidSettingsDialog::showEvent(QShowEvent *event) { readSettings(); event->accept(); } QString FluidSettingsDialog::defaultAudioDriver() const { const QString QSTR_DEFAULT_AUDIODRIVER = #if defined(Q_OS_WIN) QLatin1String("wasapi"); #elif defined(Q_OS_OSX) QLatin1String("coreaudio"); #elif defined(Q_OS_LINUX) QSTR_PULSEAUDIO; #else QLatin1String("oss"); #endif return QSTR_DEFAULT_AUDIODRIVER; } void FluidSettingsDialog::chkDriverProperties(QSettings *settings) { //qDebug() << Q_FUNC_INFO; if (m_driver != nullptr) { drumstick::rt::MIDIConnection conn; m_driver->close(); m_driver->initialize(settings); m_driver->open(conn); QVariant drivers = m_driver->property("audiodrivers"); if (drivers.isValid()) { auto text = ui->audioDriver->currentText(); ui->audioDriver->blockSignals(true); ui->audioDriver->clear(); ui->audioDriver->addItems(drivers.toStringList()); ui->audioDriver->setCurrentText(text); ui->audioDriver->blockSignals(false); } ui->lblVersion->clear(); ui->lblVersion->setText(driverVersion()); QVariant varStatus = m_driver->property("status"); if (varStatus.isValid()) { ui->lblStatus->clear(); ui->lblStatus->setText(varStatus.toBool() ? tr("Ready") : tr("Failed") ); ui->lblStatusIcon->setPixmap(varStatus.toBool() ? QPixmap(":/checked.png") : QPixmap(":/error.png") ); } } } void drumstick::widgets::FluidSettingsDialog::initBuffer() { if ((ui->audioDriver->currentText() == QSTR_PULSEAUDIO) && driverVersionLessThan_2_2_8()) { //qDebug() << Q_FUNC_INFO << QSTR_PULSEAUDIO << driverVersion(); int bufferTime = ui->bufferTime->value(); int minBufTime = ui->bufferTime->minimum(); if (bufferTime < minBufTime) { bufferTime = minBufTime; } ui->bufferTime->setValue( bufferTime ); bufferTimeChanged( bufferTime ); } else { //qDebug() << Q_FUNC_INFO; bufferSizeChanged(); } } QString FluidSettingsDialog::driverVersion() const { static QString result; if (m_driver != nullptr && result.isEmpty()) { QVariant varVersion = m_driver->property("libversion"); if (varVersion.isValid()) { result = varVersion.toString(); } } return result; } bool FluidSettingsDialog::driverVersionLessThan_2_2_8() { static const QVersionNumber check_2_2_8(2, 2, 8); QVersionNumber driverV = QVersionNumber::fromString(driverVersion()); return driverV < check_2_2_8; } void FluidSettingsDialog::readSettings() { //qDebug() << Q_FUNC_INFO; SettingsFactory settings; settings->beginGroup(QSTR_PREFERENCES); ui->audioDriver->setCurrentText( settings->value(QSTR_AUDIODRIVER, defaultAudioDriver()).toString() ); ui->bufferTime->setValue( settings->value(QSTR_BUFFERTIME, DEFAULT_BUFFERTIME).toInt() ); ui->periodSize->setValue( settings->value(QSTR_PERIODSIZE, DEFAULT_PERIODSIZE).toInt() ); ui->periods->setValue( settings->value(QSTR_PERIODS, DEFAULT_PERIODS).toInt() ); ui->sampleRate->setText( settings->value(QSTR_SAMPLERATE, DEFAULT_SAMPLERATE).toString() ); ui->chorus->setChecked( settings->value(QSTR_CHORUS, DEFAULT_CHORUS).toInt() != 0 ); ui->reverb->setChecked( settings->value(QSTR_REVERB, DEFAULT_REVERB).toInt() != 0 ); ui->gain->setText( settings->value(QSTR_GAIN, DEFAULT_GAIN).toString() ); ui->polyphony->setText( settings->value(QSTR_POLYPHONY, DEFAULT_POLYPHONY).toString() ); ui->soundFont->setText( settings->value(QSTR_INSTRUMENTSDEFINITION, m_defSoundFont).toString() ); settings->endGroup(); audioDriverChanged( ui->audioDriver->currentText() ); chkDriverProperties(settings.getQSettings()); } void FluidSettingsDialog::writeSettings() { //qDebug() << Q_FUNC_INFO; SettingsFactory settings; QString audioDriver; QString soundFont(m_defSoundFont); int bufferTime(DEFAULT_BUFFERTIME); int periodSize(DEFAULT_PERIODSIZE); int periods(DEFAULT_PERIODS); double sampleRate(DEFAULT_SAMPLERATE); int chorus(DEFAULT_CHORUS); int reverb(DEFAULT_REVERB); double gain(DEFAULT_GAIN); int polyphony(DEFAULT_POLYPHONY); audioDriver = ui->audioDriver->currentText(); if (audioDriver.isEmpty()) { audioDriver = defaultAudioDriver(); } soundFont = ui->soundFont->text(); bufferTime = ui->bufferTime->value(); periodSize = ui->periodSize->value(); periods = ui->periods->value(); sampleRate = ui->sampleRate->text().toDouble(); chorus = (ui->chorus->isChecked() ? 1 : 0); reverb = (ui->reverb->isChecked() ? 1 : 0); gain = ui->gain->text().toDouble(); polyphony = ui->polyphony->text().toInt(); settings->beginGroup(QSTR_PREFERENCES); settings->setValue(QSTR_INSTRUMENTSDEFINITION, soundFont); settings->setValue(QSTR_AUDIODRIVER, audioDriver); settings->setValue(QSTR_BUFFERTIME, bufferTime); settings->setValue(QSTR_PERIODSIZE, periodSize); settings->setValue(QSTR_PERIODS, periods); settings->setValue(QSTR_SAMPLERATE, sampleRate); settings->setValue(QSTR_CHORUS, chorus); settings->setValue(QSTR_REVERB, reverb); settings->setValue(QSTR_GAIN, gain); settings->setValue(QSTR_POLYPHONY, polyphony); settings->endGroup(); settings->sync(); chkDriverProperties(settings.getQSettings()); } void FluidSettingsDialog::restoreDefaults() { //qDebug() << Q_FUNC_INFO; ui->audioDriver->setCurrentText( defaultAudioDriver() ); ui->bufferTime->setValue( DEFAULT_BUFFERTIME ); ui->periodSize->setValue( DEFAULT_PERIODSIZE ); ui->periods->setValue( DEFAULT_PERIODS ); ui->sampleRate->setText( QString::number( DEFAULT_SAMPLERATE )); ui->chorus->setChecked( DEFAULT_CHORUS != 0 ); ui->reverb->setChecked( DEFAULT_REVERB != 0 ); ui->gain->setText( QString::number( DEFAULT_GAIN ) ); ui->polyphony->setText( QString::number( DEFAULT_POLYPHONY )); ui->soundFont->setText( m_defSoundFont ); initBuffer(); } void FluidSettingsDialog::showFileDialog() { QDir dir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR, QStandardPaths::LocateDirectory)); if (!dir.exists()) { dir = QDir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR2, QStandardPaths::LocateDirectory)); } QString fileName = QFileDialog::getOpenFileName(this, tr("Select SoundFont"), dir.absolutePath(), tr("SoundFont Files (*.sf2 *.sf3 *.dls)")); if (!fileName.isEmpty()) { ui->soundFont->setText(fileName); } } void FluidSettingsDialog::audioDriverChanged(const QString &text) { //qDebug() << Q_FUNC_INFO << text; if ((text == QSTR_PULSEAUDIO) && driverVersionLessThan_2_2_8()) { ui->bufferTime->setDisabled(false); ui->bufferTime->blockSignals(false); ui->periodSize->setDisabled(true); ui->periodSize->blockSignals(true); ui->periods->setVisible(false); ui->periods->setDisabled(true); ui->periods->blockSignals(true); } else { ui->bufferTime->setDisabled(true); ui->bufferTime->blockSignals(true); ui->periodSize->setDisabled(false); ui->periodSize->blockSignals(false); ui->periods->setVisible(true); ui->periods->setDisabled(false); ui->periods->blockSignals(false); } initBuffer(); } void FluidSettingsDialog::bufferTimeChanged(int value) { double rate = ui->sampleRate->text().toDouble(); int size = qRound( value * rate / 1000.0 ); ui->periodSize->setValue( size ); ui->periods->setValue( ui->periods->minimum() ); //qDebug() << Q_FUNC_INFO << "time:" << value << "rate:" << rate << "size:" << size; } void FluidSettingsDialog::bufferSizeChanged() { QString audioDriver = ui->audioDriver->currentText(); double rate = ui->sampleRate->text().toDouble(); int size = ui->periodSize->value(); if ((audioDriver != QSTR_PULSEAUDIO) || !driverVersionLessThan_2_2_8()) { size *= ui->periods->value(); } int ms = qRound( 1000.0 * size / rate ); ui->bufferTime->setValue(ms); //qDebug() << Q_FUNC_INFO << "time:" << ms << "rate:" << rate << "size:" << size; } void FluidSettingsDialog::changeSoundFont(const QString& fileName) { readSettings(); ui->soundFont->setText(fileName); writeSettings(); } } // namespace widgets } // namespace drumstick drumstick-2.9.0/library/widgets/fluidsettingsdialog.ui0000644000175000017500000001610714541630232022255 0ustar pedropedro drumstick::widgets::FluidSettingsDialog 0 0 319 431 FluidSynth Driver Settings Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults QFrame::StyledPanel QFrame::Raised Audio Driver: Gain: gain Reverb Sound Font: soundFont 2 64 FluidSynth Version: 64 8192 Period Size: periodSize Initialization Status: Sample Rate: sampleRate Chorus ... # of Periods: periods Polyphony: polyphony Buffer Time: ms 3 300 30 audioDriver periodSize periods sampleRate chorus reverb gain polyphony soundFont btnFile buttonBox accepted() drumstick::widgets::FluidSettingsDialog accept() 248 254 157 274 buttonBox rejected() drumstick::widgets::FluidSettingsDialog reject() 316 260 286 274 drumstick-2.9.0/library/widgets/macsynthsettingsdialog.cpp0000644000175000017500000001222514541630232023142 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include "macsynthsettingsdialog.h" #include "ui_macsynthsettingsdialog.h" #include #include /** * @file macsynthsettingsdialog.cpp * Implementation of the Mac Synth configuration dialog */ namespace drumstick { namespace widgets { MacSynthSettingsDialog::MacSynthSettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::MacSynthSettingsDialog) { ui->setupUi(this); connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::pressed, this, &MacSynthSettingsDialog::restoreDefaults); connect(ui->btn_soundfont, &QToolButton::pressed, this, &MacSynthSettingsDialog::showFileDialog); SettingsFactory settings; drumstick::rt::BackendManager man; m_driver = man.outputBackendByName("DLS Synth"); if (m_driver != nullptr) { checkDriver(settings.getQSettings()); } } MacSynthSettingsDialog::~MacSynthSettingsDialog() { if (m_driver != nullptr) { m_driver->close(); } delete ui; } void MacSynthSettingsDialog::accept() { writeSettings(); if (m_driver != nullptr) { QString title; QVariant varStatus = m_driver->property("status"); if (varStatus.isValid()) { title = varStatus.toBool() ? tr("DLS Synth Initialized") : tr("DLS Synth Initialization Failed"); QVariant varDiag = m_driver->property("diagnostics"); if (varDiag.isValid()) { QString text = varDiag.toStringList().join(QChar::LineFeed).trimmed(); if (varStatus.toBool()) { if (!text.isEmpty()) { QMessageBox::information(this, title, text); } } else { QMessageBox::critical(this, title, text); return; } } } } QDialog::accept(); } void MacSynthSettingsDialog::showEvent(QShowEvent *event) { readSettings(); event->accept(); } void MacSynthSettingsDialog::readSettings() { SettingsFactory settings; settings->beginGroup("DLS Synth"); bool reverb = settings->value("reverb_dls", true).toBool(); bool def = settings->value("default_dls", true).toBool(); QString soundfont = settings->value("soundfont_dls").toString(); settings->endGroup(); ui->reverb_dls->setChecked(reverb); ui->default_dls->setChecked(def); ui->soundfont_dls->setText(soundfont); } void drumstick::widgets::MacSynthSettingsDialog::checkDriver(QSettings* settings) { if (m_driver != nullptr) { m_driver->close(); m_driver->initialize(settings); drumstick::rt::MIDIConnection conn; m_driver->open(conn); QVariant varStatus = m_driver->property("status"); if (varStatus.isValid()) { ui->lblStatusText->clear(); ui->lblStatusText->setText(varStatus.toBool() ? tr("Ready") : tr("Failed") ); ui->lblStatusIcon->setPixmap(varStatus.toBool() ? QPixmap(":/checked.png") : QPixmap(":/error.png") ); } } } void MacSynthSettingsDialog::writeSettings() { SettingsFactory settings; QString soundfont = ui->soundfont_dls->text(); bool reverb = ui->reverb_dls->isChecked(); bool def = ui->default_dls->isChecked(); settings->beginGroup("DLS Synth"); settings->setValue("soundfont_dls", soundfont); settings->setValue("reverb_dls", reverb); settings->setValue("default_dls", def); settings->endGroup(); settings->sync(); checkDriver(settings.getQSettings()); } void MacSynthSettingsDialog::restoreDefaults() { ui->reverb_dls->setChecked(false); ui->default_dls->setChecked(true); ui->soundfont_dls->clear(); } void MacSynthSettingsDialog::changeSoundFont(const QString& fileName) { readSettings(); ui->soundfont_dls->setText(fileName); writeSettings(); } void MacSynthSettingsDialog::showFileDialog() { QDir dir = (QDir::homePath() + "/Library/Audio/Sounds/Banks/"); QString fileName = QFileDialog::getOpenFileName(this, tr("Select SoundFont"), dir.absolutePath(), tr("SoundFont Files (*.sf2 *.dls)")); if (!fileName.isEmpty()) { ui->soundfont_dls->setText(fileName); } } } // namespace widgets } // namespace drumstick drumstick-2.9.0/library/widgets/macsynthsettingsdialog.ui0000644000175000017500000001160014541630232022771 0ustar pedropedro drumstick::widgets::MacSynthSettingsDialog 0 0 319 182 macOS Synth Driver Settings Qt::Vertical 20 256 true 0 0 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults SoundFont: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter soundfont_dls Default Apple DLS Sound Set ... Use Internal Reverb Init. Status: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter soundfont_dls btn_soundfont default_dls reverb_dls default_dls toggled(bool) soundfont_dls setDisabled(bool) 136 58 260 31 default_dls toggled(bool) btn_soundfont setDisabled(bool) 294 58 294 30 default_dls toggled(bool) lblSoundFont setDisabled(bool) 96 58 66 31 buttonBox accepted() drumstick::widgets::MacSynthSettingsDialog accept() 170 158 192 184 buttonBox rejected() drumstick::widgets::MacSynthSettingsDialog reject() 259 155 281 179 drumstick-2.9.0/library/widgets/networksettingsdialog.cpp0000644000175000017500000001415714541630232023013 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include "networksettingsdialog.h" #include "ui_networksettingsdialog.h" #include #include /** * @file networksettingsdialog.cpp * Implementation of the Network configuration dialog */ namespace drumstick { namespace widgets { const QString NetworkSettingsDialog::QSTR_ADDRESS_IPV4 = QStringLiteral("225.0.0.37"); const QString NetworkSettingsDialog::QSTR_ADDRESS_IPV6 = QStringLiteral("ff12::37"); NetworkSettingsDialog::NetworkSettingsDialog(const bool forInput, QWidget *parent) : QDialog(parent), ui(new Ui::NetworkSettingsDialog), m_input(forInput) { ui->setupUi(this); connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::pressed, this, &NetworkSettingsDialog::restoreDefaults); connect(ui->checkIPv6, &QCheckBox::toggled, this, &NetworkSettingsDialog::toggledIPv6); drumstick::rt::BackendManager man; if (m_input) { m_driver = man.inputBackendByName("Network"); } else { m_driver = man.outputBackendByName("Network"); } } NetworkSettingsDialog::~NetworkSettingsDialog() { if (m_driver != nullptr) { if (m_input) { static_cast(m_driver)->close(); } else { static_cast(m_driver)->close(); } } delete ui; } void NetworkSettingsDialog::accept() { writeSettings(); if (m_driver != nullptr) { QString title; QVariant varStatus = m_driver->property("status"); if (varStatus.isValid()) { title = varStatus.toBool() ? tr("Network Initialized") : tr("Network Initialization Failed"); QVariant varDiag = m_driver->property("diagnostics"); if (varDiag.isValid()) { QString text = varDiag.toStringList().join(QChar::LineFeed).trimmed(); if (varStatus.toBool()) { if (!text.isEmpty()) { QMessageBox::information(this, title, text); } } else { QMessageBox::critical(this, title, text); return; } } } } QDialog::accept(); } void NetworkSettingsDialog::showEvent(QShowEvent *event) { readSettings(); event->accept(); } void NetworkSettingsDialog::readSettings() { SettingsFactory settings; settings->beginGroup("Network"); QString ifaceName = settings->value("interface", QString()).toString(); bool ipv6 = settings->value("ipv6", false).toBool(); QString address = settings->value("address", ipv6 ? QSTR_ADDRESS_IPV6 : QSTR_ADDRESS_IPV4).toString(); settings->endGroup(); ui->txtAddress->setText(address); ui->checkIPv6->setChecked(ipv6); ui->comboInterface->addItem(tr("Any"), ""); foreach( const QNetworkInterface& iface, QNetworkInterface::allInterfaces() ) { if ( iface.isValid() && iface.flags().testFlag(QNetworkInterface::CanMulticast) && iface.flags().testFlag(QNetworkInterface::IsUp) && iface.flags().testFlag(QNetworkInterface::IsRunning) && !iface.flags().testFlag(QNetworkInterface::IsLoopBack) ) { QString name = iface.name(); QString text = iface.humanReadableName(); ui->comboInterface->addItem(text, name); if (name == ifaceName) { ui->comboInterface->setCurrentText(text); } } } chkInitialization(settings.getQSettings()); } void NetworkSettingsDialog::writeSettings() { SettingsFactory settings; QString networkAddr = ui->txtAddress->text(); QString networkIface = ui->comboInterface->currentData().toString(); bool ipv6 = ui->checkIPv6->isChecked(); settings->beginGroup("Network"); settings->setValue("address", networkAddr); settings->setValue("interface", networkIface); settings->setValue("ipv6", ipv6); settings->endGroup(); settings->sync(); chkInitialization(settings.getQSettings()); } void NetworkSettingsDialog::chkInitialization(QSettings *settings) { if (m_driver != nullptr) { drumstick::rt::MIDIConnection conn("21928", 21928); if (m_input) { auto d = static_cast(m_driver); d->close(); d->initialize(settings); d->open(conn); } else { auto d = static_cast(m_driver); d->close(); d->initialize(settings); d->open(conn); } QVariant varStatus = m_driver->property("status"); if (varStatus.isValid()) { ui->lblStatusText->clear(); ui->lblStatusText->setText(varStatus.toBool() ? tr("Ready") : tr("Failed") ); ui->lblStatusIcon->setPixmap(varStatus.toBool() ? QPixmap(":/checked.png") : QPixmap(":/error.png") ); } } } void NetworkSettingsDialog::restoreDefaults() { ui->checkIPv6->setChecked(false); ui->txtAddress->setText(QSTR_ADDRESS_IPV4); ui->comboInterface->setCurrentText(tr("Any")); } void NetworkSettingsDialog::toggledIPv6(bool checked) { ui->txtAddress->setText(checked ? QSTR_ADDRESS_IPV6 : QSTR_ADDRESS_IPV4); } } // namespace widgets } // namespace drumstick drumstick-2.9.0/library/widgets/networksettingsdialog.ui0000644000175000017500000000665514541630232022652 0ustar pedropedro drumstick::widgets::NetworkSettingsDialog 0 0 325 210 Network Driver Settings 0 0 :/checked.png Init. Status: 0 0 Use IPv6 Network Interface: comboInterface Address Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults buttonBox accepted() drumstick::widgets::NetworkSettingsDialog accept() 236 126 157 87 buttonBox rejected() drumstick::widgets::NetworkSettingsDialog reject() 292 126 286 87 drumstick-2.9.0/library/widgets/pianokey.h0000644000175000017500000000461514541630232017643 0ustar pedropedro/* Virtual Piano Widget for Qt Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #ifndef PIANOKEY_H_ #define PIANOKEY_H_ #include #include /** * @file pianokey.h * Declaration of the PianoKey class */ namespace drumstick { namespace widgets { class PianoPalette; class PianoKey : public QGraphicsRectItem { public: explicit PianoKey(QGraphicsItem * parent = nullptr ): QGraphicsRectItem(parent), m_pressed(false), m_note(0), m_black(false), m_usePixmap(true) { } PianoKey(const QRectF &rect, const bool black, const int note); void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override; int getNote() const { return m_note; } void setBrush(const QBrush& b) { m_brush = b; } void setPressedBrush(const QBrush& b) { m_selectedBrush = b; } void resetBrush(); bool isPressed() const { return m_pressed; } void setPressed(bool p); int getDegree() const { return m_note % 12; } int getType() const { return (m_black ? 1 : 0); } bool isBlack() const { return m_black; } const QPixmap& getPixmap() const; void setPixmap(const QPixmap& p); QRectF pixmapRect() const; bool getUsePixmap() const; void setUsePixmap(bool usePixmap); void paintPixmap(QPixmap &pixmap, const QColor& color) const; static const PianoPalette keyPalette; private: bool m_pressed; QBrush m_selectedBrush; QBrush m_brush; int m_note; bool m_black; QPixmap m_pixmap; bool m_usePixmap; }; }} // namespace drumstick::widgets #endif /*PIANOKEY_H_*/ drumstick-2.9.0/library/widgets/pianokeybd.qrc0000644000175000017500000000035114541630232020500 0ustar pedropedro blkey.png whkey.png checked.png error.png drumstick-2.9.0/library/widgets/pianopalette.cpp0000644000175000017500000003721414541630232021045 0ustar pedropedro/* MIDI Virtual Piano Keyboard Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include /** * @file pianopalette.cpp * Implementation of the Piano Palette */ namespace drumstick { namespace widgets { /** * @brief PianoPalette::QSTR_PALETTEPREFIX is the string prefix for all * the settings stored as persisting settings */ const QString PianoPalette::QSTR_PALETTEPREFIX = QStringLiteral("Palette_"); /** * @brief PianoPalette::PianoPalette Constructor * @param id The Palette Policy identifier */ PianoPalette::PianoPalette(int id) : m_paletteId(id) { initialize(); resetColors(); retranslateStrings(); } /** * @brief PianoPalette::initialize reserves and initializes space to store colors * according to the palette policy identifier */ void PianoPalette::initialize() { int maxcolors = 0; switch(m_paletteId) { case PAL_SINGLE: maxcolors = 1; break; case PAL_DOUBLE: maxcolors = 2; break; case PAL_CHANNELS: maxcolors = 16; break; case PAL_SCALE: maxcolors = 12; break; case PAL_KEYS: maxcolors = 2; break; case PAL_FONT: maxcolors = 4; break; case PAL_HISCALE: maxcolors = 12; break; default: return; } m_colors.reserve(maxcolors); m_names.reserve(maxcolors); for(int i=0; ipalette().highlight().color()); } /** * @brief PianoPalette::resetPaletteDouble resets the colors to the standard values * for the PAL_DOUBLE palette policy */ void PianoPalette::resetPaletteDouble() { setColor(0, tr("N"), qApp->palette().highlight().color()); setColor(1, tr("#"), QColor("lawngreen")); } /** * @brief PianoPalette::resetPaletteChannels resets the colors to the standard values * for the PAL_CHANNELS palette policy */ void PianoPalette::resetPaletteChannels() { setColor(0, tr("1"), QColor("red")); setColor(1, tr("2"), QColor("lime")); setColor(2, tr("3"), QColor("blue")); setColor(3, tr("4"), QColor("gold")); setColor(4, tr("5"), QColor("maroon")); setColor(5, tr("6"), QColor("green")); setColor(6, tr("7"), QColor("navy")); setColor(7, tr("8"), QColor("darkorange")); setColor(8, tr("9"), QColor("purple")); setColor(9, tr("10"), qApp->palette().highlight().color()); setColor(10, tr("11"), QColor("teal")); setColor(11, tr("12"), QColor("chocolate")); setColor(12, tr("13"), QColor("fuchsia")); setColor(13, tr("14"), QColor("olivedrab")); setColor(14, tr("15"), QColor("aqua")); setColor(15, tr("16"), QColor("greenyellow")); } /** * @brief PianoPalette::resetPaletteScale resets the colors to the standard values * for the PAL_SCALE palette policy */ void PianoPalette::resetPaletteScale() { /* R G B C 100% - - 0 C# 100% 50% - 1 D 100% 100% - 2 D# 50% 100% - 3 E - 100% - 4 F - 100% 50% 5 F# - 100% 100% 6 G - 50% 100% 7 G# - - 100% 8 A 50% - 100% 9 A# 100% - 100% 10 B 100% - 50% 11 */ setColor(0, tr("C"), QColor::fromRgb(255,0,0)); setColor(1, tr("C#"), QColor::fromRgb(255,127,0)); setColor(2, tr("D"), QColor::fromRgb(255,255,0)); setColor(3, tr("D#"), QColor::fromRgb(127,255,0)); setColor(4, tr("E"), QColor::fromRgb(0,255,0)); setColor(5, tr("F"), QColor::fromRgb(0,255,127)); setColor(6, tr("F#"), QColor::fromRgb(0,255,255)); setColor(7, tr("G"), QColor::fromRgb(0,127,255)); setColor(8, tr("G#"), QColor::fromRgb(0,0,255)); setColor(9, tr("A"), QColor::fromRgb(127,0,255)); setColor(10, tr("A#"), QColor::fromRgb(255,0,255)); setColor(11, tr("B"), QColor::fromRgb(255,0,127)); } /** * @brief PianoPalette::resetPaletteKeys resets the colors to the standard values * for the PAL_KEYS palette policy */ void PianoPalette::resetPaletteKeys() { setColor(0, tr("N"), QColor("white")); setColor(1, tr("#"), QColor("black")); } /** * @brief PianoPalette::resetPaletteFont resets the colors to the standard values * for the PAL_FONT palette policy */ void PianoPalette::resetPaletteFont() { setColor(0, tr("N"), QColor("black")); setColor(1, tr("#"), QColor("white")); setColor(2, tr("N*"), QColor("white")); setColor(3, tr("#*"), QColor("white")); } /** * @brief PianoPalette::retranslateStrings retranslates the names and description * texts according to the palette policy */ void PianoPalette::retranslateStrings() { switch(m_paletteId) { case PAL_SINGLE: setPaletteName(tr("Single color highlight")); setPaletteText(tr("A single color to highlight all note events")); retranslatePaletteSingle(); break; case PAL_DOUBLE: setPaletteName(tr("Two colors highlight")); setPaletteText(tr("One color to highlight natural notes and a different one for accidentals")); retranslatePaletteDouble(); break; case PAL_CHANNELS: setPaletteName(tr("MIDI Channels highlight")); setPaletteText(tr("A different color to highlight each MIDI channel. Enable Omni mode in the MIDI IN connection")); retranslatePaletteChannels(); break; case PAL_SCALE: setPaletteName(tr("Chromatic scale background")); setPaletteText(tr("One color for each note in the chromatic scale")); retranslatePaletteScale(); break; case PAL_KEYS: setPaletteName(tr("Keys background")); setPaletteText(tr("One color for natural notes and another for accidentals")); retranslatePaletteKeys(); break; case PAL_FONT: setPaletteName(tr("Font foreground")); setPaletteText(tr("Colors for note names")); retranslatePaletteFont(); break; case PAL_HISCALE: setPaletteName(tr("Chromatic scale highlight")); setPaletteText(tr("One color for each note in the chromatic scale")); retranslatePaletteScale(); break; default: return; } } /** * @brief PianoPalette::retranslatePaletteSingle retranslates the color names * for the PAL_SINGLE palette policy */ void PianoPalette::retranslatePaletteSingle() { setColorName(0, QString()); } /** * @brief PianoPalette::retranslatePaletteDouble retranslates the color names * for the PAL_DOUBLE palette policy */ void PianoPalette::retranslatePaletteDouble() { setColorName(0, tr("N")); setColorName(1, tr("#")); } /** * @brief PianoPalette::retranslatePaletteChannels retranslates the color names * for the PAL_CHANNELS palette policy */ void PianoPalette::retranslatePaletteChannels() { setColorName(0, tr("1")); setColorName(1, tr("2")); setColorName(2, tr("3")); setColorName(3, tr("4")); setColorName(4, tr("5")); setColorName(5, tr("6")); setColorName(6, tr("7")); setColorName(7, tr("8")); setColorName(8, tr("9")); setColorName(9, tr("10")); setColorName(10, tr("11")); setColorName(11, tr("12")); setColorName(12, tr("13")); setColorName(13, tr("14")); setColorName(14, tr("15")); setColorName(15, tr("16")); } /** * @brief PianoPalette::retranslatePaletteScale retranslates the color names * for the PAL_SCALE palette policy */ void PianoPalette::retranslatePaletteScale() { setColorName(0, tr("C")); setColorName(1, tr("C#")); setColorName(2, tr("D")); setColorName(3, tr("D#")); setColorName(4, tr("E")); setColorName(5, tr("F")); setColorName(6, tr("F#")); setColorName(7, tr("G")); setColorName(8, tr("G#")); setColorName(9, tr("A")); setColorName(10, tr("A#")); setColorName(11, tr("B")); } /** * @brief PianoPalette::retranslatePaletteKeys retranslates the color names * for the PAL_KEYS palette policy */ void PianoPalette::retranslatePaletteKeys() { setColorName(0, tr("N")); setColorName(1, tr("#")); } /** * @brief PianoPalette::retranslatePaletteFont retranslates the color names * for the PAL_FONT palette policy */ void PianoPalette::retranslatePaletteFont() { setColorName(0, tr("N")); setColorName(1, tr("#")); setColorName(2, tr("N*")); setColorName(3, tr("#*")); } /** * @brief PianoPalette::isHighLight palette function * @return true if the palette is used for keys highlighting */ bool PianoPalette::isHighLight() const { return (m_paletteId == PAL_SINGLE) || (m_paletteId == PAL_DOUBLE) || (m_paletteId == PAL_CHANNELS) || (m_paletteId == PAL_HISCALE); } /** * @brief PianoPalette::isBackground palette function * @return true if the palette is used for painting the keys background */ bool PianoPalette::isBackground() const { return (m_paletteId == PAL_SCALE) || (m_paletteId == PAL_KEYS); } /** * @brief PianoPalette::isForeground palette function * @return true if the palette is used to paint text over the keys */ bool PianoPalette::isForeground() const { return (m_paletteId == PAL_FONT); } /** * @brief PianoPalette::paletteId palette policy * @return the palette policy identifier */ int PianoPalette::paletteId() const { return m_paletteId; } /** * @brief PianoPalette::setColor changes a palette color * @param n the color number * @param s the color name * @param c the color value */ void PianoPalette::setColor(const int n, const QString& s, const QColor& c) { if (n < m_colors.size()) { m_colors[n] = c; m_names[n] = s; } } /** * @brief PianoPalette::setColor changes a palette color * @param n the color number * @param c the color value */ void PianoPalette::setColor(const int n, const QColor& c) { if (n < m_colors.size()) m_colors[n] = c; } /** * @brief PianoPalette::setColorName changes a palette color name * @param n the color number * @param s the color name */ void PianoPalette::setColorName(const int n, const QString& s) { if (n < m_names.size()) m_names[n] = s; } /** * @brief PianoPalette::getColor gets a palette color * @param i the color number * @return the color value */ QColor PianoPalette::getColor(const int i) const { if (i < m_colors.size()) return m_colors[i]; return {}; } /** * @brief PianoPalette::getColorName gets a palette color name * @param i the color number * @return the color name */ QString PianoPalette::getColorName(const int i) const { if (i < m_names.size()) return m_names[i]; return QString(); } /** * @brief PianoPalette::getNumColors palette policy colors size * @return the number of colors represented by the palette */ int PianoPalette::getNumColors() const { return m_colors.size(); } /** * @brief PianoPalette::paletteName palette policy name * @return the name of the palette */ QString PianoPalette::paletteName() const { return m_paletteName; } /** * @brief PianoPalette::setPaletteName changes the palette name * @param name new name of the palette */ void PianoPalette::setPaletteName(const QString& name) { if (m_paletteName != name) { m_paletteName = name; } } /** * @brief PianoPalette::paletteText gets the palette description * @return new description of the palette */ QString PianoPalette::paletteText() const { return m_paletteText; } /** * @brief PianoPalette::setPaletteText changes the palette description * @param help new palette description string */ void PianoPalette::setPaletteText(const QString& help) { m_paletteText = help; } /** * @brief PianoPalette::saveColors stores the set of colors as persistent settings */ void PianoPalette::saveColors() const { SettingsFactory settings; settings->beginWriteArray(QSTR_PALETTEPREFIX + QString::number(m_paletteId)); for(int i=0; isetArrayIndex(i); settings->setValue("color", m_colors[i]); } settings->endArray(); settings->sync(); } /** * @brief PianoPalette::loadColors loads the set of colors from persistent settings */ void PianoPalette::loadColors() { SettingsFactory settings; int size = settings->beginReadArray(QSTR_PALETTEPREFIX + QString::number(m_paletteId)); if (size > m_colors.size()) size = m_colors.size(); for(int i=0; isetArrayIndex(i); QColor c = settings->value("color", QColor()).value(); setColor(i, c); } settings->endArray(); } /** * @brief PianoPalette::operator == compares two palettes * @param other another palette object * @return true if both palettes are equal */ bool PianoPalette::operator==(const PianoPalette& other) const { return (m_paletteId == other.m_paletteId) && (m_colors == other.m_colors); } /** * @brief PianoPalette::operator != compares two palettes * @param other another palette object * @return true if both palettes are different */ bool PianoPalette::operator!=(const PianoPalette &other) const { return !(*this == other); } /** * @brief Serialize a PianoPalette instance into a QDataStream * @param stream a QDataStream * @param palette instance * @return the QDataStream */ QDataStream &operator<<(QDataStream &stream, const PianoPalette &palette) { stream << palette.m_paletteId; stream << palette.m_colors; stream << palette.m_names; stream << palette.m_paletteName; stream << palette.m_paletteText; return stream; } /** * @brief Deserialize a PianoPalette instance from a QDataStream * @param stream a QDataStream * @param palette instance * @return the QDataStream */ QDataStream &operator>>(QDataStream &stream, PianoPalette &palette) { stream >> palette.m_paletteId; stream >> palette.m_colors; stream >> palette.m_names; stream >> palette.m_paletteName; stream >> palette.m_paletteText; return stream; } } // namespace widgets } // namespace drumstick drumstick-2.9.0/library/widgets/translations/0000755000175000017500000000000014541630232020366 5ustar pedropedrodrumstick-2.9.0/library/widgets/translations/drumstick-widgets_cs.ts0000644000175000017500000005773314541630232025113 0ustar pedropedro PianoPalette N Č # # 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 C C C# Cis D D D# Dis E E F F F# Fis G G G# Gis A A A# Ais B H N* Č* #* #* Single color highlight Zvýraznění jednou barvou A single color to highlight all note events Jedna barva pro zvýraznění všech událostí not Two colors highlight Zvýraznění dvěma barvami One color to highlight natural notes and a different one for accidentals Jedna barva pro zvýraznění odrážek a jiná pro posuvky MIDI Channels highlight Zvýraznění kanálů MIDI A different color to highlight each MIDI channel. Enable Omni mode in the MIDI IN connection Různá barva pro zvýraznění každého MIDI kanálu. Povolte režim Omni v připojení MIDI IN Chromatic scale background Pozadí chromatické stupnice One color for each note in the chromatic scale Jedna barva pro každou notu v chromatické stupnici Keys background Pozadí předznamenání One color for natural notes and another for accidentals Jedna barva pro noty odrážek a jiná pro posuvky Font foreground Popředí písma Colors for note names Barvy pro názvy not Chromatic scale highlight Zvýraznění chromatické stupnice drumstick::widgets::FluidSettingsDialog FluidSynth Driver Settings Nastavení ovladače FluidSynth Sample Rate: Vzorkovací kmitočet: Period Size: Velikost období: # of Periods: # období: Audio Driver: Ovladač zvuku: Polyphony: Vícehlas: FluidSynth Version: Verze FluidSynth: Sound Font: Zvuková banka: Initialization Status: Stav inicializace: Buffer Time: Čas vyrovnávací paměti: ms ms Gain: Zesílení: ... ... Chorus Sbor Reverb Dozvuk FluidSynth Initialized FluidSynth inicializován FluidSynth Initialization Failed FluidSynth se nepodařilo inicializovat Ready Připraven Failed Selhalo Select SoundFont Vybrat zvukovou banku SoundFont Files (*.sf2 *.sf3 *.dls) SoundFont Files (*.sf2) Soubory zvukových bank (*.sf2) drumstick::widgets::MacSynthSettingsDialog macOS Synth Driver Settings Nastavení ovladače syntetizátoru macOS SoundFont: Zvuková banka: ... ... Use Internal Reverb Použít vnitřní dozvuk Init. Status: Stav inicializace: Default Apple DLS Sound Set Výchozí zvuková sada Apple DLS DLS Synth Initialized DLS Synth inicializován DLS Synth Initialization Failed DLS Synth se nepodařilo inicializovat Ready Připraven Failed Selhalo Select SoundFont Vybrat zvukovou banku SoundFont Files (*.sf2 *.dls) Soubory zvukových bank (*.sf2 *.dls) drumstick::widgets::NetworkSettingsDialog Network Driver Settings Nastavení síťového ovladače Init. Status: Stav inicializace: Use IPv6 Použít IPv6 Network Interface: Síťové rozhraní: Address Adresa Network Initialized Síť inicializována Network Initialization Failed Síť se nepodařilo inicializovat Any Vše Ready Připraven Failed Selhalo drumstick::widgets::PianoScene C C C♯ C♯ D D D♯ D♯ E E F F F♯ F♯ G G G♯ G♯ A A A♯ A♯ B H D♭ D♭ E♭ E♭ G♭ G♭ A♭ A♭ B♭ B drumstick::widgets::SonivoxSettingsDialog Sonivox EAS Synth Sonivox EAS Synth Chorus Sbor Reverb Dozvuk Buffer Time: Čas vyrovnávací paměti: ms ms Init. Status: Stav inicializace: Sonivox Initialized Sonivox inicializován Sonivox Initialization Failed Sonivox se nepodařilo inicializovat Ready Připraven Failed Selhalo drumstick-2.9.0/library/widgets/translations/drumstick-widgets_de.ts0000644000175000017500000005725114541630232025071 0ustar pedropedro PianoPalette N # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 C C C# C♯ D D D# D♯ E E F F F# F♯ G G G# G♯ A A A# A♯ B H N* #* Single color highlight Eine Farbe für alles A single color to highlight all note events Eine einzige Farbe für alle Noten-Events Two colors highlight One color to highlight natural notes and a different one for accidentals Eine Farbe für Stammtöne, eine andere für Halbtöne MIDI Channels highlight Farbe nach MIDI-Kanal A different color to highlight each MIDI channel. Enable Omni mode in the MIDI IN connection Eine andere Farbe für jeden MIDI-Kanal. Hierzu den Omni-Modus in der MIDI IN Verbindung aktivieren Chromatic scale background Chromatische Farbskala One color for each note in the chromatic scale Verschiedene Farben nach Noten in der Skala Keys background Tasten-Hintergrund One color for natural notes and another for accidentals Eine Farbe für Stammtöne, eine andere für Halbtöne Font foreground Schriftfarbe Colors for note names Farben für Notennamen Chromatic scale highlight Chromatische Farbskala (hervorgehoben) drumstick::widgets::FluidSettingsDialog FluidSynth Driver Settings FluidSynth Treiber-Einstellungen Sample Rate: Period Size: # of Periods: Audio Driver: Audio-Treiber: Polyphony: Polyphonie: FluidSynth Version: Sound Font: Initialization Status: Buffer Time: Puffer-Zeit: ms Gain: ... Chorus Reverb Hall FluidSynth Initialized FluidSynth Initialization Failed Ready Failed Select SoundFont SoundFont auswählen SoundFont Files (*.sf2 *.sf3 *.dls) SoundFont Files (*.sf2) SoundFont-Dateien (*.sf2) drumstick::widgets::MacSynthSettingsDialog macOS Synth Driver Settings macOS Treiber-Einstellungen SoundFont: ... Use Internal Reverb Internen Hall benutzen Init. Status: Default Apple DLS Sound Set DLS Synth Initialized DLS Synth Initialization Failed Ready Failed Select SoundFont SoundFont auswählen SoundFont Files (*.sf2 *.dls) SoundFont-Dateien (*.sf2) drumstick::widgets::NetworkSettingsDialog Network Driver Settings Netzwerk Treiber-Einstellungen Init. Status: Use IPv6 IPv6 benutzen Network Interface: Netzwerk-Schnittstelle: Address Adresse Network Initialized Network Initialization Failed Any Alle Ready Failed drumstick::widgets::PianoScene C C C♯ C♯ D D D♯ D♯ E E F F F♯ F♯ G G G♯ G♯ A A A♯ A♯ B H D♭ D♭ E♭ E♭ G♭ G♭ A♭ A♭ B♭ B drumstick::widgets::SonivoxSettingsDialog Sonivox EAS Synth Chorus Reverb Hall Buffer Time: Puffer-Zeit: ms Init. Status: Sonivox Initialized Sonivox Initialization Failed Ready Failed drumstick-2.9.0/library/widgets/translations/drumstick-widgets_en.ts0000644000175000017500000006053014541630232025075 0ustar pedropedro PianoPalette N # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 C C# D D# E F F# G G# A A# B N* #* Single color highlight A single color to highlight all note events Two colors highlight One color to highlight natural notes and a different one for accidentals MIDI Channels highlight A different color to highlight each MIDI channel. Enable Omni mode in the MIDI IN connection Chromatic scale background One color for each note in the chromatic scale Keys background One color for natural notes and another for accidentals Font foreground Colors for note names Chromatic scale highlight drumstick::widgets::FluidSettingsDialog FluidSynth Driver Settings Sample Rate: Period Size: # of Periods: Audio Driver: Polyphony: FluidSynth Version: Sound Font: Initialization Status: Buffer Time: ms Gain: ... Chorus Reverb FluidSynth Initialized FluidSynth Initialization Failed Ready Failed Select SoundFont SoundFont Files (*.sf2 *.sf3 *.dls) drumstick::widgets::MacSynthSettingsDialog macOS Synth Driver Settings SoundFont: ... Use Internal Reverb Init. Status: Default Apple DLS Sound Set DLS Synth Initialized DLS Synth Initialization Failed Ready Failed Select SoundFont SoundFont Files (*.sf2 *.dls) drumstick::widgets::NetworkSettingsDialog Network Driver Settings Init. Status: Use IPv6 Network Interface: Address Network Initialized Network Initialization Failed Any Ready Failed drumstick::widgets::PianoScene C C♯ D D♯ E F F♯ G G♯ A A♯ B D♭ E♭ G♭ A♭ B♭ drumstick::widgets::SonivoxSettingsDialog Sonivox EAS Synth Chorus Reverb Buffer Time: ms Init. Status: Sonivox Initialized Sonivox Initialization Failed Ready Failed drumstick-2.9.0/library/widgets/translations/drumstick-widgets_es.ts0000644000175000017500000006014314541630232025102 0ustar pedropedro PianoPalette N N # # 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 C do C# do# D re D# re# E mi F fa F# fa# G sol G# sol# A la A# la# B si N* N* #* #* Single color highlight Color único de resaltado A single color to highlight all note events Un único color para resaltar todos los eventos de notas Two colors highlight Dos colores de resaltado One color to highlight natural notes and a different one for accidentals Un color para resaltar las notas naturales y otro diferente para las alteraciones MIDI Channels highlight Resaltado por Canales MIDI A different color to highlight each MIDI channel. Enable Omni mode in the MIDI IN connection Un color diferente para resaltar cada canal MIDI. Habilitar modo Omni en la conexión MIDI IN Chromatic scale background Escala cromática (fondode teclas) One color for each note in the chromatic scale Un color para cada nota de la escala cromática Keys background Fondo de teclas One color for natural notes and another for accidentals Un color para las notas naturales y otro para las accidentales Font foreground Primer plano de fuente Colors for note names Colores para los nombres de notas Chromatic scale highlight Escala cromática (resaltado) drumstick::widgets::FluidSettingsDialog FluidSynth Driver Settings Opciones del controlador de FluidSynth Sample Rate: Frecuencia de muestreo: Period Size: Tamaño del periodo: # of Periods: Número de periodos: Audio Driver: Controlador de audio: Polyphony: Polifonía: FluidSynth Version: Versión de Fluidsynth: Sound Font: SoundFont: Initialization Status: Estado de inicialización: Buffer Time: Tiempo de buffer: ms ms Gain: Ganancia: ... ... Chorus Coral Reverb Reverberación FluidSynth Initialized FluidSynth inicializado FluidSynth Initialization Failed Inicialización fallida de FluidSynth Ready Listo Failed Fallido Select SoundFont Seleccionar SoundFont SoundFont Files (*.sf2 *.sf3 *.dls) Archivos SoundFont (*.sf2 *.sf3 *.dls) SoundFont Files (*.sf2) Archivos SoundFont (*.sf2) drumstick::widgets::MacSynthSettingsDialog macOS Synth Driver Settings Opciones del controlador del sintetizador de macOS SoundFont: SoundFont: ... ... Use Internal Reverb Utilizar reverberación interna Init. Status: Estado de inicialización: Default Apple DLS Sound Set Conjunto de sonidos DLS de Apple por omisión DLS Synth Initialized Sintetizador DLS inicializado DLS Synth Initialization Failed Inicialización fallida del sintetizador DLS Ready Listo Failed Fallido Select SoundFont Seleccionar SoundFont SoundFont Files (*.sf2 *.dls) Archivos SoundFont (*.sf2 *.dls) drumstick::widgets::NetworkSettingsDialog Network Driver Settings Opciones del controlador de red Init. Status: Estado de inicialización: Use IPv6 Usar IPv6 Network Interface: Interfaz de red: Address Dirección Network Initialized Red inicializada Network Initialization Failed Inicialización fallida de la red Any Cualquiera Ready Listo Failed Fallido drumstick::widgets::PianoScene C do C♯ do♯ D re D♯ re♯ E mi F fa F♯ fa♯ G sol G♯ sol♯ A la A♯ la♯ B si D♭ re♭ E♭ mi♭ G♭ sol♭ A♭ la♭ B♭ si♭ drumstick::widgets::SonivoxSettingsDialog Sonivox EAS Synth Sintetizador Sonivox EAS Chorus Coral Reverb Reverberación Buffer Time: Tiempo de buffer: ms ms Init. Status: Estado de inicialización: Sonivox Initialized Sonivox inicializado Sonivox Initialization Failed Inicialización fallida de Sonivox Ready Listo Failed Fallido drumstick-2.9.0/library/widgets/translations/drumstick-widgets_fr.ts0000644000175000017500000005776114541630232025116 0ustar pedropedro PianoPalette N N # # 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 C Do C# Do# D Re D# Re# E Mi F Fa F# Fa# G Sol G# Sol# A La A# La# B Si N* N* #* #* Single color highlight Couleur unique A single color to highlight all note events Une seule couleur pour tous les évênements de notes Two colors highlight Deux couleurs One color to highlight natural notes and a different one for accidentals Une couleur pour les notes naturelles, une autre pour les notes altérées MIDI Channels highlight Canaux MIDI A different color to highlight each MIDI channel. Enable Omni mode in the MIDI IN connection Couleur différente selon le canal MIDI. Activer le mode Omni pour la connexion MIDI IN Chromatic scale background Echelle chromatique One color for each note in the chromatic scale Une couleur pour chaque note dans l'échelle chromatique Keys background Couleur des touches One color for natural notes and another for accidentals Une couleur pour les notes naturelles, une autre pour les notes altérées Font foreground Couleur du texte Colors for note names Couleurs des noms des notes Chromatic scale highlight Echelle chromatique drumstick::widgets::FluidSettingsDialog FluidSynth Driver Settings Paramètres du pilote FluidSynth Sample Rate: Fréquence d'échantillonnage: Period Size: Taille d'une période: # of Periods: Nb Périodes: Audio Driver: Pilote Audio: Polyphony: Polyphonie: FluidSynth Version: Sound Font: Soundfont : Initialization Status: Buffer Time: Durée tampon: ms ms Gain: Gain: ... ... Chorus Chorus Reverb Réverbération FluidSynth Initialized FluidSynth Initialization Failed Ready Failed Select SoundFont Choisir un SoundFont SoundFont Files (*.sf2 *.sf3 *.dls) SoundFont Files (*.sf2) Fichiers SoundFont (*.sf2) drumstick::widgets::MacSynthSettingsDialog macOS Synth Driver Settings Paramètres du pilote de synthé macOS SoundFont: Soundfont : ... ... Use Internal Reverb Utiliser la réverberation interne Init. Status: Default Apple DLS Sound Set Apple DLS Sound Set standard DLS Synth Initialized DLS Synth Initialization Failed Ready Failed Select SoundFont Choisir un SoundFont SoundFont Files (*.sf2 *.dls) Fichiers SoundFont (*.sf2 *.dls) drumstick::widgets::NetworkSettingsDialog Network Driver Settings Paramètres du pilote réseau Init. Status: Use IPv6 Utiliser l'IPv6 Network Interface: Interface réseau: Address Adresse Network Initialized Network Initialization Failed Any Toutes Ready Failed drumstick::widgets::PianoScene C Do C♯ Do♯ D Re D♯ Re♯ E Mi F Fa F♯ Fa♯ G Sol G♯ Sol♯ A La A♯ La♯ B Si D♭ Re♭ E♭ Mi♭ G♭ Sol♭ A♭ La♭ B♭ Si♭ drumstick::widgets::SonivoxSettingsDialog Sonivox EAS Synth Sonivox EAS Synth Chorus Chorus Reverb Réverbération Buffer Time: Durée tampon: ms ms Init. Status: Sonivox Initialized Sonivox Initialization Failed Ready Failed drumstick-2.9.0/library/widgets/translations/drumstick-widgets_gl.ts0000644000175000017500000006001714541630232025075 0ustar pedropedro PianoPalette N N # # 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 C Do C# Do# D Re D# Re# E Mi F Fa F# Fa# G Sol G# Sol# A La A# La# B Si N* N* #* #* Single color highlight Salientar unha soa cor A single color to highlight all note events Unha única cor para resaltar todas as accións das notas Two colors highlight Salientar dúas cores One color to highlight natural notes and a different one for accidentals Unha cor para resaltar as notas naturais e outro diferente para as alteracións MIDI Channels highlight Salientar canles MIDI A different color to highlight each MIDI channel. Enable Omni mode in the MIDI IN connection Unha cor diferente para resaltar cada canle MIDI. Activa o modo Omni na conexión MIDI IN Chromatic scale background Escala cromática de fondo One color for each note in the chromatic scale Unha cor para cada nota da escala cromática Keys background Fondo das teclas One color for natural notes and another for accidentals Unha cor para as notas naturais e outro diferente para as alteracións Font foreground Cor do texto Colors for note names Cores para nomes de notas Chromatic scale highlight Salientar escala cromática drumstick::widgets::FluidSettingsDialog FluidSynth Driver Settings Axustes do controlador FluidSynth Sample Rate: Frecuencia de mostraxe: Period Size: Tamaña dos período: # of Periods: Núm. de períodos: Audio Driver: Controlador de son: Polyphony: Polifonía: FluidSynth Version: Versión de FluidSynth: Sound Font: Son fonte: Initialization Status: Estado de inicialización: Buffer Time: Tempo de buffer: ms ms Gain: Ganancia: ... ... Chorus Coros Reverb Reverberación FluidSynth Initialized FluidSynth Inicializado FluidSynth Initialization Failed Inicialización fallida de FluidSynth Ready Listo Failed Fallou Select SoundFont Seleccione SoundFont SoundFont Files (*.sf2 *.sf3 *.dls) Ficheiros SoundFont (*.sf2 *.sf3 *.dls) SoundFont Files (*.sf2) Ficheiros SoundFont (*.sf2) drumstick::widgets::MacSynthSettingsDialog macOS Synth Driver Settings Axustes do controlador de sintetizador de macOS SoundFont: SoundFont: ... ... Use Internal Reverb Usar a reverberación interna Init. Status: Estado de inicialización: Default Apple DLS Sound Set Sonidos DLS predeterminados de Apple DLS Synth Initialized DLS Synth Iniciado DLS Synth Initialization Failed Fallou a inicialización do DLS Synth Ready Listo Failed Fallou Select SoundFont Seleccione SoundFont SoundFont Files (*.sf2 *.dls) Ficheiros SoundFont (*.sf2 *.dls) drumstick::widgets::NetworkSettingsDialog Network Driver Settings Axustes do controlador de rede Init. Status: Estado de inicialización: Use IPv6 Usar IPv6 Network Interface: Interface de rede: Address Enderezo Network Initialized Rede inicializada Network Initialization Failed Fallou a inicialización da rede Any Calquera Ready Listo Failed Fallou drumstick::widgets::PianoScene C Do C♯ Do♯ D Re D♯ Re♯ E Mi F Fa F♯ Fa♯ G Sol G♯ Sol♯ A La A♯ La♯ B Si D♭ Re♭ E♭ Mi♭ G♭ Sol♭ A♭ La♭ B♭ Si♭ drumstick::widgets::SonivoxSettingsDialog Sonivox EAS Synth Sintetizador Sonivox EAS Chorus Coros Reverb Reverberación Buffer Time: Tempo de buffer: ms ms Init. Status: Estado de inicialización: Sonivox Initialized Sonivox inicializado Sonivox Initialization Failed Fallou a inicialización de Sonivox Ready Listo Failed Fallou drumstick-2.9.0/library/widgets/translations/drumstick-widgets_it.ts0000644000175000017500000005765214541630232025122 0ustar pedropedro PianoPalette N N # # 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 C do C# do# D re D# re# E mi F fa F# fa# G sol G# sol# A la A# la# B si N* N* #* #* Single color highlight Evidenziazione a colore singolo A single color to highlight all note events Un singolo colore per evidenziare tutti gli eventi-nota Two colors highlight Evidenziazione a due colori One color to highlight natural notes and a different one for accidentals Un colore per evidenziare le note naturali e uno differente per gli accidenti MIDI Channels highlight Evidenziazione dei canali MIDI A different color to highlight each MIDI channel. Enable Omni mode in the MIDI IN connection Un colore diverso per evidenziare ogni canale MIDI. Abilita il modo Omni nella connessione MIDI IN Chromatic scale background Sfondo della scala cromatica One color for each note in the chromatic scale Un colore per ogni nota della scala cromatica Keys background Sfondo dei tasti One color for natural notes and another for accidentals Un colore per le note naturali e un altro per gli accidenti Font foreground Colore del carattere Colors for note names Colore dei nomi delle note Chromatic scale highlight Evidenziazione della scala cromatica drumstick::widgets::FluidSettingsDialog FluidSynth Driver Settings Impostazioni del driver Fuidsynth Sample Rate: Tasso di campionamento: Period Size: Dimensione del periodo: # of Periods: # di Periodi: Audio Driver: Driver audio: Polyphony: Polifonia: FluidSynth Version: Versione di Fluidsynth: Sound Font: Banco di campioni: Initialization Status: Stato di inizializzazione: Buffer Time: Durata del buffer: ms ms Gain: Guadagno: ... ... Chorus Coro Reverb Riverbero FluidSynth Initialized FluidSynth inizializzato FluidSynth Initialization Failed Inizializzazione di FluidSynth fallita Ready Pronto Failed Errore Select SoundFont Seleziona un campione audio SoundFont Files (*.sf2 *.sf3 *.dls) File di campioni audio (*.sf2 *.sf3 *.dls) drumstick::widgets::MacSynthSettingsDialog macOS Synth Driver Settings Impostazioni del Synth driver macOS SoundFont: Banco di campioni: ... ... Use Internal Reverb Usa Riverbero interno Init. Status: Stato di inizializzazione: Default Apple DLS Sound Set Apple DLS set di souni di default DLS Synth Initialized DLS Synth inizializzato DLS Synth Initialization Failed Inizializzazione di DLS Synth fallita Ready Pronto Failed Errore Select SoundFont Selezionare un campione audio SoundFont Files (*.sf2 *.dls) File di campioni audio (*.sf2 *.dls) drumstick::widgets::NetworkSettingsDialog Network Driver Settings Impostrazioni del driver di network Init. Status: Stato di inizializzazione: Use IPv6 Usa IPv6 Network Interface: Interfaccia di network: Address Indirizzo Network Initialized Network inizializzato Network Initialization Failed Inizializzazione del network fallita Any Qualsiasi Ready Pronto Failed Errore drumstick::widgets::PianoScene C do C♯ do♯ D re D♯ re♯ E mi F fa F♯ fa♯ G sol G♯ sol♯ A la A♯ la♯ B si D♭ re♭ E♭ mi♭ G♭ sol♭ A♭ la♭ B♭ si♭ drumstick::widgets::SonivoxSettingsDialog Sonivox EAS Synth Sonivox EAS Synth Chorus Coro Reverb Reverbero Buffer Time: Durata del buffer: ms ms Init. Status: Stato di inizializzazione: Sonivox Initialized Sonivox inizializzato Sonivox Initialization Failed Inizializzazione di Sonivox fallita Ready Pronto Failed Errore drumstick-2.9.0/library/widgets/translations/drumstick-widgets_nl.ts0000644000175000017500000005574214541630232025115 0ustar pedropedro PianoPalette N # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 C C# D D# E F F# G G# A A# B N* #* Single color highlight A single color to highlight all note events Two colors highlight One color to highlight natural notes and a different one for accidentals MIDI Channels highlight A different color to highlight each MIDI channel. Enable Omni mode in the MIDI IN connection Chromatic scale background One color for each note in the chromatic scale Keys background One color for natural notes and another for accidentals Font foreground Colors for note names Chromatic scale highlight drumstick::widgets::FluidSettingsDialog FluidSynth Driver Settings Sample Rate: Period Size: # of Periods: Audio Driver: Polyphony: FluidSynth Version: Sound Font: Initialization Status: Buffer Time: ms Gain: ... ... Chorus Reverb FluidSynth Initialized FluidSynth Initialization Failed Ready Failed Select SoundFont SoundFont Files (*.sf2 *.sf3 *.dls) drumstick::widgets::MacSynthSettingsDialog macOS Synth Driver Settings SoundFont: ... ... Use Internal Reverb Init. Status: Default Apple DLS Sound Set DLS Synth Initialized DLS Synth Initialization Failed Ready Failed Select SoundFont SoundFont Files (*.sf2 *.dls) drumstick::widgets::NetworkSettingsDialog Network Driver Settings Init. Status: Use IPv6 Network Interface: Address Network Initialized Network Initialization Failed Any Ready Failed drumstick::widgets::PianoScene C C♯ D D♯ E F F♯ G G♯ A A♯ B D♭ E♭ G♭ A♭ B♭ drumstick::widgets::SonivoxSettingsDialog Sonivox EAS Synth Chorus Reverb Buffer Time: ms Init. Status: Sonivox Initialized Sonivox Initialization Failed Ready Failed drumstick-2.9.0/library/widgets/translations/drumstick-widgets_ru.ts0000644000175000017500000006117414541630232025126 0ustar pedropedro PianoPalette N N # # 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 C До C# До # D Ре D# Ре # E Ми F Фа F# Фа # G Соль G# Соль # A Ля A# Ля # B Си N* N* #* #* Single color highlight Подсветка одним цветом A single color to highlight all note events Один цвет для подсветки всех событий нот Two colors highlight Подсветка двумя цветами One color to highlight natural notes and a different one for accidentals Один цвет подсвечивает натуральные ноты, второй акцентированные MIDI Channels highlight Подсветка каналов MIDI A different color to highlight each MIDI channel. Enable Omni mode in the MIDI IN connection Разные цвета подсветки каждого канала MIDI. Включить режим Omni в соединении MIDI IN Chromatic scale background Хроматический фон One color for each note in the chromatic scale Один цвет для каждой ноты по хроматической шкале Keys background Фон клавиш One color for natural notes and another for accidentals Один цвет для натуральных нот и один для акцентированных Font foreground Цвет шрифта Colors for note names Цвета для названий нот Chromatic scale highlight Подсветка хроматической шкалой drumstick::widgets::FluidSettingsDialog FluidSynth Driver Settings Настройки драйвера FluidSynth Sample Rate: Скорость сэмплов: Period Size: Размер периода: # of Periods: Число периодов: Audio Driver: Аудиодрайвер: Polyphony: Полифония: FluidSynth Version: Sound Font: Sound Font: Initialization Status: Buffer Time: Время буфера: ms мс Gain: Усиление: ... ... Chorus Хорус Reverb Реверберация FluidSynth Initialized FluidSynth Initialization Failed Ready Failed Select SoundFont Выбрать SoundFont SoundFont Files (*.sf2 *.sf3 *.dls) SoundFont Files (*.sf2) Файлы SoundFont (*.sf2) drumstick::widgets::MacSynthSettingsDialog macOS Synth Driver Settings Настройки драйвера macOS Synth SoundFont: SoundFont: ... ... Use Internal Reverb Использовать встроенную реверберацию Init. Status: Default Apple DLS Sound Set Набор звуков Apple DLS по умолчанию DLS Synth Initialized DLS Synth Initialization Failed Ready Failed Select SoundFont Выбор SoundFont SoundFont Files (*.sf2 *.dls) Файлы SoundFont (*.sf2 *.dls) drumstick::widgets::NetworkSettingsDialog Network Driver Settings Настройки драйвера сети Init. Status: Use IPv6 Использовать IPv6 Network Interface: Сетевой интерфейс: Address Адрес Network Initialized Network Initialization Failed Any Любой Ready Failed drumstick::widgets::PianoScene C До C♯ До♯ D Ре D♯ Ре♯ E Ми F Фа F♯ Фа♯ G Соль G♯ Соль♯ A Ля A♯ Ля♯ B Си D♭ Ре♭ E♭ Ми♭ G♭ Соль♭ A♭ Ля♭ B♭ Си♭ drumstick::widgets::SonivoxSettingsDialog Sonivox EAS Synth Sonivox EAS Synth Chorus Хорус Reverb Реверберация Buffer Time: Время буфера: ms мс Init. Status: Sonivox Initialized Sonivox Initialization Failed Ready Failed drumstick-2.9.0/library/widgets/translations/drumstick-widgets_sr.ts0000644000175000017500000005763014541630232025126 0ustar pedropedro PianoPalette N N # # 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 C C C# C# D D D# D# E E F F F# F# G G G# G# A A A# A# B B N* #* Single color highlight A single color to highlight all note events Једнобојно означавање догађаја свих нота Two colors highlight One color to highlight natural notes and a different one for accidentals Прва боја означава тонове, а друга полутонове MIDI Channels highlight A different color to highlight each MIDI channel. Enable Omni mode in the MIDI IN connection Chromatic scale background One color for each note in the chromatic scale Једна боја за сваку ноту хроматске скале Keys background One color for natural notes and another for accidentals Font foreground Colors for note names Chromatic scale highlight drumstick::widgets::FluidSettingsDialog FluidSynth Driver Settings Флуид-синт — поставке Sample Rate: Учестаност узорковања: Period Size: Величина периода: # of Periods: # периода: Audio Driver: Зв. посредник: Polyphony: Полифонија: FluidSynth Version: Sound Font: Звукотека: Initialization Status: Buffer Time: ms мс Gain: Јачина: ... ... Chorus Хорус Reverb Јека FluidSynth Initialized FluidSynth Initialization Failed Ready Failed Select SoundFont Избор звукотеке SoundFont Files (*.sf2 *.sf3 *.dls) SoundFont Files (*.sf2) Звукотеке (*.sf2) drumstick::widgets::MacSynthSettingsDialog macOS Synth Driver Settings SoundFont: Звукотека: ... ... Use Internal Reverb Init. Status: Default Apple DLS Sound Set DLS Synth Initialized DLS Synth Initialization Failed Ready Failed Select SoundFont Избор звукотеке SoundFont Files (*.sf2 *.dls) Звукотеке (*.sf2 *.dls) drumstick::widgets::NetworkSettingsDialog Network Driver Settings Поставке мрежног посредника Init. Status: Use IPv6 Network Interface: Мрежни прикључак: Address Адреса Network Initialized Network Initialization Failed Any Било који Ready Failed drumstick::widgets::PianoScene C C C♯ C♯ D D D♯ D♯ E E F F F♯ F♯ G G G♯ G♯ A A A♯ A♯ B B D♭ D♭ E♭ E♭ G♭ G♭ A♭ A♭ B♭ B drumstick::widgets::SonivoxSettingsDialog Sonivox EAS Synth Chorus Хорус Reverb Јека Buffer Time: ms мс Init. Status: Sonivox Initialized Sonivox Initialization Failed Ready Failed drumstick-2.9.0/library/widgets/translations/drumstick-widgets_sv.ts0000644000175000017500000005754214541630232025134 0ustar pedropedro PianoPalette N N # # 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 C C C# Ciss D D D# Diss E E F F F# Fiss G G G# Giss A A A# Aiss B H N* N* #* #* Single color highlight Enfärgsmarkering A single color to highlight all note events En färg för att markera alla tonhändelser Two colors highlight Tvåfärgsmarkering One color to highlight natural notes and a different one for accidentals En färg för att markera stamtoner och en annan för grentoner MIDI Channels highlight MIDI-kanalmarkering A different color to highlight each MIDI channel. Enable Omni mode in the MIDI IN connection Olika färger för att markera varje MIDI-kanal. Aktivera omniläget i MIDI-IN-anslutningen. Chromatic scale background Bakgrund för kromatisk skala One color for each note in the chromatic scale En färg för varje ton i den kromatiska skalan Keys background Tangentbakgrund One color for natural notes and another for accidentals En färg för stamtoner och en annan för grentoner. Font foreground Typsnittsförgrund Colors for note names Färger för tonnamn Chromatic scale highlight Markering för kromatisk skala drumstick::widgets::FluidSettingsDialog FluidSynth Driver Settings Drivrutinsinställningar för FluidSynth Sample Rate: Samplingsfrekvens: Period Size: Periodstorlek: # of Periods: Antal perioder: Audio Driver: Ljuddrivrutin: Polyphony: Polyfoni: FluidSynth Version: FluidSynth-version: Sound Font: Ljudfont: Initialization Status: Initialiseringsstatus: Buffer Time: Buffringstid: ms ms Gain: Volym: ... ... Chorus Korus Reverb Eko FluidSynth Initialized FluidSynth initialiserad FluidSynth Initialization Failed FluidSynth-initialiseringen misslyckades Ready Redo Failed Misslyckades Select SoundFont Välj ljudfont SoundFont Files (*.sf2 *.sf3 *.dls) SoundFont Files (*.sf2) SoundFont-filer (*.sf2) drumstick::widgets::MacSynthSettingsDialog macOS Synth Driver Settings Syntdrivrutinsinställningar för macOS SoundFont: Ljudfont: ... ... Use Internal Reverb Använd internt eko Init. Status: Initialiseringsstatus: Default Apple DLS Sound Set Förvald Apple DLS ljuduppsättning DLS Synth Initialized DLS-synt initialiserad DLS Synth Initialization Failed DLS-syntinitialiseringen misslyckades Ready Redo Failed Misslyckades Select SoundFont Välj ljudfont SoundFont Files (*.sf2 *.dls) SoundFont-filer (*.sf2 *.dls) drumstick::widgets::NetworkSettingsDialog Network Driver Settings Drivrutinsinställningar för nätverk Init. Status: Initialiseringsstatus: Use IPv6 Network Interface: Nätverksgränssnitt: Address Adress Network Initialized Nätverk initialiserat Network Initialization Failed Nätverksinitialiseringen misslyckades Any Någon Ready Redo Failed Misslyckades drumstick::widgets::PianoScene C C C♯ Ciss D D D♯ Diss E E F F F♯ Fiss G G G♯ Giss A A A♯ Aiss B H D♭ Dess E♭ Ess G♭ Gess A♭ Ass B♭ B drumstick::widgets::SonivoxSettingsDialog Sonivox EAS Synth Sonivox EAS Synt Chorus Korus Reverb Eko Buffer Time: Buffringstid: ms ms Init. Status: Initialiseringsstatus: Sonivox Initialized Sonivox initialiserad Sonivox Initialization Failed Sonivoxinitialisering misslyckades Ready Redo Failed Misslyckades drumstick-2.9.0/library/widgets/translations/drumstick-widgets_tr.ts0000644000175000017500000005576314541630232025134 0ustar pedropedro PianoPalette N # 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 C C# D D# E F F# G G# A A# B N* #* Single color highlight A single color to highlight all note events Two colors highlight One color to highlight natural notes and a different one for accidentals MIDI Channels highlight A different color to highlight each MIDI channel. Enable Omni mode in the MIDI IN connection Chromatic scale background One color for each note in the chromatic scale Keys background One color for natural notes and another for accidentals Font foreground Colors for note names Chromatic scale highlight drumstick::widgets::FluidSettingsDialog FluidSynth Driver Settings Sample Rate: Period Size: # of Periods: Audio Driver: Polyphony: FluidSynth Version: Sound Font: Initialization Status: Buffer Time: ms Gain: ... Chorus Reverb FluidSynth Initialized FluidSynth Initialization Failed Ready Failed Select SoundFont SoundFont Files (*.sf2 *.sf3 *.dls) drumstick::widgets::MacSynthSettingsDialog macOS Synth Driver Settings SoundFont: ... Use Internal Reverb Init. Status: Default Apple DLS Sound Set DLS Synth Initialized DLS Synth Initialization Failed Ready Failed Select SoundFont SoundFont Files (*.sf2 *.dls) drumstick::widgets::NetworkSettingsDialog Network Driver Settings Init. Status: Use IPv6 Network Interface: Address Network Initialized Network Initialization Failed Any Ready Failed drumstick::widgets::PianoScene C C♯ D D♯ E F F♯ G G♯ A A♯ B D♭ E♭ G♭ A♭ B♭ drumstick::widgets::SonivoxSettingsDialog Sonivox EAS Synth Chorus Reverb Buffer Time: ms Init. Status: Sonivox Initialized Sonivox Initialization Failed Ready Failed drumstick-2.9.0/library/widgets/translations/drumstick-widgets_zh_CN.ts0000644000175000017500000006102014541630232025467 0ustar pedropedro PianoPalette N # 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 C C C# D D D# E E F F F# G G G# A A A# B B N* #* Single color highlight 单色高亮 A single color to highlight all note events 用一种颜色高亮所有音符事件 Two colors highlight 双色高亮 One color to highlight natural notes and a different one for accidentals 一种颜色用于突出自然音,另一种颜色用于变化音 MIDI Channels highlight MIDI 通道高亮 A different color to highlight each MIDI channel. Enable Omni mode in the MIDI IN connection 用不同颜色突出显示每个 MIDI 通道。在 MIDI 输入 连接中启用全通道模式 Chromatic scale background 半音阶背景色 One color for each note in the chromatic scale 半音阶中的每个音符都用一种颜色表示 Keys background 琴键背景色 One color for natural notes and another for accidentals 一种颜色用于自然音,另一种用于变化音 Font foreground 字体前景色 Colors for note names 用于音符名称的颜色 Chromatic scale highlight 半音阶高亮 drumstick::widgets::FluidSettingsDialog FluidSynth Driver Settings FluidSynth 驱动设置 Sample Rate: 采样率: Period Size: 周期大小: # of Periods: 周期数量: Audio Driver: 音频驱动: Polyphony: 复音数: FluidSynth Version: FluidSynth 版本: Sound Font: 音色库: Initialization Status: 初始化状态: Buffer Time: 缓冲时间: ms 毫秒 Gain: 增益调节: ... ... Chorus 合唱(Chorus) Reverb 混响(Reverb) FluidSynth Initialized FluidSynth 已初始化 FluidSynth Initialization Failed FluidSynth 初始化失败 Ready 就绪 Failed 失败 Select SoundFont 选择音色库 SoundFont Files (*.sf2 *.sf3 *.dls) 音色库文件 (*.sf2 *.sf3 *.dls) drumstick::widgets::MacSynthSettingsDialog macOS Synth Driver Settings macOS Synth 驱动设置 SoundFont: 音色库: ... ... Use Internal Reverb 使用内部混响 Init. Status: 初始化状态: Default Apple DLS Sound Set 使用苹果 DLS 音效 DLS Synth Initialized DLS Synth 已初始化 DLS Synth Initialization Failed DLS Synth 初始化失败 Ready 就绪 Failed 失败 Select SoundFont 选择音色库 SoundFont Files (*.sf2 *.dls) 音色库文件 (*.sf2 *.dls) drumstick::widgets::NetworkSettingsDialog Network Driver Settings 网络驱动设置 Init. Status: 初始化状态: Use IPv6 使用 IPv6 Network Interface: 网络接口: Address 地址 Network Initialized 网络已初始化 Network Initialization Failed 网络初始化失败 Any 任意 Ready 就绪 Failed 失败 drumstick::widgets::PianoScene C C C♯ C♯ D D D♯ D♯ E E F F F♯ F♯ G G G♯ G♯ A A A♯ A♯ B B D♭ D♭ E♭ E♭ G♭ G♭ A♭ A♭ B♭ B♭ drumstick::widgets::SonivoxSettingsDialog Sonivox EAS Synth Chorus 合唱(Chorus) Reverb 混响(Reverb) SoundFont: 音色库: ... ... Sonivox Library Version: Sonivox 库版本: Buffer Time: 缓冲时间: ms 毫秒 Init. Status: 初始化状态: Sonivox Initialized Sonivox 已初始化 Sonivox Initialization Failed Sonivox 初始化失败 Ready 就绪 Failed 失败 Select SoundFont 选择音色库 SoundFont Files (*.dls) 音色库文件 (*.dls) drumstick-2.9.0/library/widgets/whkey.png0000644000175000017500000001041514541630232017503 0ustar pedropedroPNG  IHDR97ZWsBIT|d pHYs+tEXtSoftwarewww.inkscape.org<RtEXtCopyrightCC Attribution-ShareAlike http://creativecommons.org/licenses/by-sa/4.0/Tb,IDATxuwA6B#T[b,E $DSI&ƐH@c4KLcLTM!l%&PPJ Zi?ә;wt·+ife3}y=sl$yYl}2[aI'y '{ 'y|'I˷ +X7$8G7]'1&$KXst66Ǔe7*{j mEx7瘢~A$8fܒwsdش]i_OyZgtp@%y"I~.|eՃfNrg?= M$e{$ej3 ԗ>7CΰSbm/I$Wn\3I^a|kIUXd#e b$fئV'{,8AݳOdXѾ# ?}6ɻnz8Ϭ'Iޟayw^[2lyԣ5ܿ'ZwyAodgG>,X3_8O&b%ÑNΠ_ 82dw7AHnBuJ4k33pZvoH ǚi؂_L#3A4gObDpOdџwed "pM2/W|w2aOn\yAߝZg73YxNpPy׼f\2w`}O/X$o&&xPAϾ8|0u5WOtHekk73\ޓS ? G;4NEkG\"K#hKA[C3v$x3Eɷ\Y3v5Sx6ɓ`%9#hZld=ވ+2NB0"h*"h*fvr *"h*"h*'t *"h*"h*2Z MASET4UMASET4UMA3Vsg MASET4UMASET4UMA3f{f MASET4UMASET4UMASET4UMASET4UMASET4UMeMASET4UMASET4UMAbq.)#h*"h*"hjl"h*"h*"h*"h*"h*]' *"h*"h*l"2"h*"h*fNASFT4UMASET4UMASET4UMASET4UMASET4UMA3f{N4UMASET4UMASET4U-4UMASET4UMASET4UMA`{[T4UMASET4UMASET4-&)#h*"h*"h.)#h*"h*"h13-h*"h*"h5[(h*"h*"h۳fl^zJT4UMASET4UMASET4cpPT4UMASET4UMASET4UX͝4UMASET4UMASET4U͘-4UMASET4UMASET4U-4UMASET4UMASET4UMASET4UMASET4UMASET4-&"2"h*"h&A3Vsg MASET4UMASET4UMA3f{f MASET4UMASET4UMASET4UMASET4UMASET4UMeMASET4UMASET4UMAbq.)#h*"h*"h;-h*"h*"h*"h*"h*] *"h*"h*y^&h*"h*"hDЌwMMASET4UMASET4UMASET4UMASET4UMASET4cBASET4UMASET4UMASET4ck[T4UMASET4UMASET4 g MASET4UMASET4UMAb2"h*"h*"hj"'"h*"h*="h*"h*]"h*"h*i=[(hfetMASET4UMASET4UMAbqZ8-h*"h*"h;[(h*"h*"h13[(h*"h*"h*fvM *"h*"h*l"h*"h*"h1.)#h*"h*"h;-h*"h*"h13[(h*"h*"h5[(h*"h*"h*fll"h*"h*"hDЌoASET4UMASET4UMASEЌBASET4UMASET4UMASEЌٞBASET4Ul,QЌfXCS*+djN o$l&y*əʢ$9%w9 M4Nn<,z{ DIĆ;m%yd3CI.=\ |c%'38.$[C3&.$m&7wxP}E{7ܓ䚓,weA[Csڭwe/o&J;xWӠ`e'A<^vgܘ#;~8я l3+sSmv>h'yю rM4-$yla;z٬ $y\a;pO%y Y}˓|&Ѣ]V=In=q]/uIfvἠȰ7.8 {-V]sqzgoIH%5ޘ$foXcrRp*7f8;y7. $Wʤ2FIGIwe*p[::8+ܜ]UIϜS˂$$Xubυ/K$9S*ʰ&]uIv%C0$[I~+Cpf image/svg+xml drumstick-2.9.0/library/widgets/keylabel.cpp0000644000175000017500000000542614541630232020150 0ustar pedropedro/* Virtual Piano Widget for Qt Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include "keylabel.h" #include "pianokey.h" #include /** * @file keylabel.cpp * Implementation of the KeyLabel class */ namespace drumstick { namespace widgets { KeyLabel::KeyLabel(QGraphicsItem *parent) : QGraphicsTextItem(parent) { setAcceptedMouseButtons(Qt::NoButton); } void KeyLabel::adjust() { qreal ax, ay; QRectF kr, br; PianoKey* key = static_cast(parentItem()); kr = key->boundingRect(); br = boundingRect(); ax = kr.x(); ay = kr.height() - 5; if (key->isBlack()) { ay -= 70; } if (rotation() == 0) { ax += (kr.width() - br.width()) / 2; ay -= br.height(); } else { ax += (kr.width() - br.height()) / 2; } setPos(ax, ay); m_savedColor = defaultTextColor(); } void KeyLabel::setOrientation(LabelOrientation ori) { if (m_orientation != ori) { m_orientation = ori; switch(m_orientation) { case VerticalOrientation: setRotation(270); break; case HorizontalOrientation: setRotation(0); break; case AutomaticOrientation: default: calculateRotation(); break; } } } void KeyLabel::restoreColor() { if (m_savedColor.isValid()) { setDefaultTextColor(m_savedColor); } } void drumstick::widgets::KeyLabel::calculateRotation() { PianoKey* key = static_cast(parentItem()); QRectF kr, br; kr = key->boundingRect(); br = boundingRect(); if (br.width() > kr.width()) { setRotation(270); } else { setRotation(0); } } void KeyLabel::setPlainText(const QString &text) { QGraphicsTextItem::setPlainText(text); adjustSize(); if (m_orientation == AutomaticOrientation) { calculateRotation(); } } void KeyLabel::setHtml(const QString &text) { QGraphicsTextItem::setHtml(text); adjustSize(); if (m_orientation == AutomaticOrientation) { calculateRotation(); } } } // namespace widgets } // namespace drumstick drumstick-2.9.0/library/widgets/keylabel.h0000644000175000017500000000271014541630232017606 0ustar pedropedro/* Virtual Piano Widget for Qt Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #ifndef KEYLABEL_H #define KEYLABEL_H #include #include /** * @file keylabel.h * Declaration of the KeyLabel class */ namespace drumstick { namespace widgets { class KeyLabel : public QGraphicsTextItem { public: explicit KeyLabel(QGraphicsItem *parent = nullptr); virtual ~KeyLabel() = default; void setPlainText(const QString& text); void adjust(); void setOrientation(LabelOrientation ori); void restoreColor(); void setHtml(const QString& text); private: LabelOrientation m_orientation = HorizontalOrientation; void calculateRotation(); QColor m_savedColor; }; }} // namespace drumstick::widgets #endif // KEYLABEL_H drumstick-2.9.0/library/widgets/pianokey.cpp0000644000175000017500000000661714541630232020202 0ustar pedropedro/* Virtual Piano Widget for Qt Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include #include "pianokey.h" /** * @file pianokey.cpp * Implementation of the PianoKey class */ namespace drumstick { namespace widgets { const PianoPalette PianoKey::keyPalette(PAL_KEYS); PianoKey::PianoKey(const QRectF &rect, const bool black, const int note) : QGraphicsRectItem(rect), m_pressed(false), m_note(note), m_black(black), m_usePixmap(true) { m_brush = keyPalette.getColor(black ? 1 : 0); setAcceptedMouseButtons(Qt::NoButton); setFlag(QGraphicsItem::ItemClipsChildrenToShape); } void PianoKey::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) { static const QPen blackPen(Qt::black, 1); painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); if (m_pressed) { if (m_selectedBrush.style() != Qt::NoBrush) { painter->setBrush(m_selectedBrush); } else { painter->setBrush(QApplication::palette().highlight()); } } else { painter->setBrush(m_brush); } painter->setPen(blackPen); painter->drawRoundedRect(rect(), 20, 15, Qt::RelativeSize); if (m_usePixmap) { QPixmap p = getPixmap(); painter->drawPixmap(rect(), p, p.rect()); } } void PianoKey::setPressed(bool p) { if (p != m_pressed) { m_pressed = p; update(); } } const QPixmap& PianoKey::getPixmap() const { static QPixmap blpixmap(QStringLiteral(":/vpiano/blkey.png")); static QPixmap whpixmap(QStringLiteral(":/vpiano/whkey.png")); static QColor bgColor; if (!m_black && (bgColor != m_brush.color())) { bgColor = m_brush.color(); paintPixmap(whpixmap, QColor::fromRgba(bgColor.rgba()^0xffffff)); } if (m_pixmap.isNull()) { return m_black ? blpixmap : whpixmap; } else { return m_pixmap; } } QRectF PianoKey::pixmapRect() const { return getPixmap().rect(); } void PianoKey::resetBrush() { m_brush = keyPalette.getColor(m_black ? 1 : 0); } void PianoKey::setPixmap(const QPixmap &p) { m_pixmap = p; } bool PianoKey::getUsePixmap() const { return m_usePixmap; } void PianoKey::setUsePixmap(bool usePixmap) { m_usePixmap = usePixmap; } void PianoKey::paintPixmap(QPixmap &pixmap, const QColor& color) const { if (!pixmap.isNull()) { QPainter painter(&pixmap); painter.setRenderHints(QPainter::SmoothPixmapTransform | QPainter::Antialiasing); painter.setCompositionMode(QPainter::CompositionMode_SourceIn); painter.fillRect(pixmap.rect(), color); } } } // namespace widgets } // namespace drumstick drumstick-2.9.0/library/widgets/pianokeybd.cpp0000644000175000017500000006431514541630232020507 0ustar pedropedro/* Virtual Piano Widget for Qt Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include "pianoscene.h" /** * @file pianokeybd.cpp * Implementation of the PianoKeybd class */ /** * @class QGraphicsView * The QGraphicsView class provides a widget for displaying the contents of a QGraphicsScene. * @see https://doc.qt.io/qt-5/qgraphicsview.html */ inline void initResources() { Q_INIT_RESOURCE(pianokeybd); } namespace drumstick { namespace widgets { /** * Global Default Alphanumeric Keyboard Map */ KeyboardMap g_DefaultKeyMap { {Qt::Key_Z, 12}, {Qt::Key_S, 13}, {Qt::Key_X, 14}, {Qt::Key_D, 15}, {Qt::Key_C, 16}, {Qt::Key_V, 17}, {Qt::Key_G, 18}, {Qt::Key_B, 19}, {Qt::Key_H, 20}, {Qt::Key_N, 21}, {Qt::Key_J, 22}, {Qt::Key_M, 23}, {Qt::Key_Q, 24}, {Qt::Key_2, 25}, {Qt::Key_W, 26}, {Qt::Key_3, 27}, {Qt::Key_E, 28}, {Qt::Key_R, 29}, {Qt::Key_5, 30}, {Qt::Key_T, 31}, {Qt::Key_6, 32}, {Qt::Key_Y, 33}, {Qt::Key_7, 34}, {Qt::Key_U, 35}, {Qt::Key_I, 36}, {Qt::Key_9, 37}, {Qt::Key_O, 38}, {Qt::Key_0, 39}, {Qt::Key_P, 40} }; /** * Global Default Raw Keyboard Map */ KeyboardMap g_DefaultRawKeyMap { #if defined(Q_OS_LINUX) {94, 11}, {52, 12}, {39, 13}, {53, 14}, {40, 15}, {54, 16}, {55, 17}, {42, 18}, {56, 19}, {43, 20}, {57, 21}, {44, 22}, {58, 23}, {59, 24}, {46, 25}, {60, 26}, {47, 27}, {61, 28}, {24, 29}, {11, 30}, {25, 31}, {12, 32}, {26, 33}, {13, 34}, {27, 35}, {28, 36}, {15, 37}, {29, 38}, {16, 39}, {30, 40}, {31, 41}, {18, 42}, {32, 43}, {19, 44}, {33, 45}, {20, 46}, {34, 47}, {35, 48} #endif #if defined(Q_OS_WIN) {86, 11}, {44, 12}, {31, 13}, {45, 14}, {32, 15}, {46, 16}, {47, 17}, {34, 18}, {48, 19}, {35, 20}, {49, 21}, {36, 22}, {50, 23}, {51, 24}, {38, 25}, {52, 26}, {39, 27}, {53, 28}, {16, 29}, {3, 30}, {17, 31}, {4, 32}, {18, 33}, {5, 34}, {19, 35}, {20, 36}, {7, 37}, {21, 38}, {8, 39}, {22, 40}, {23, 41}, {10, 42}, {24, 43}, {11, 44}, {25, 45}, {12, 46}, {26, 47}, {27, 48} #endif #if defined(Q_OS_MAC) {50, 11}, {6, 12}, {1, 13}, {7, 14}, {2, 15}, {8, 16}, {9, 17}, {5, 18}, {11, 19}, {4, 20}, {45, 21}, {38, 22}, {46, 23}, {43, 24}, {37, 25}, {47, 26}, {41, 27}, {44, 28}, {12, 29}, {19, 30}, {13, 31}, {20, 32}, {14, 33}, {21, 34}, {15, 35}, {17, 36}, {22, 37}, {16, 38}, {26, 39}, {32, 40}, {34, 41}, {25, 42}, {31, 43}, {29, 44}, {35, 45}, {27, 46}, {33, 47}, {30, 48} #endif }; class PianoKeybd::PianoKeybdPrivate { public: PianoKeybdPrivate(): m_rotation(0), m_scene(nullptr), m_rawMap(nullptr) { } ~PianoKeybdPrivate() = default; int m_rotation; PianoScene *m_scene; KeyboardMap *m_rawMap; }; /** * @brief Constructor * * This is the usual constructor when using QtDesigner and without providing * custom settings, using the default octave, number of keys and starting key. * @param parent Widget's parent */ PianoKeybd::PianoKeybd(QWidget *parent) : QGraphicsView(parent), d(new PianoKeybdPrivate()) { initialize(); initScene(DEFAULTBASEOCTAVE, DEFAULTNUMBEROFKEYS, DEFAULTSTARTINGKEY); } /** * @brief Constructor providing not only a parent widget, but also * custom values for octave, number of keys and starting key. * @param baseOctave The base octave number * @param numKeys The number of displayed keys * @param startKey The startup key * @param parent The widget's parent */ PianoKeybd::PianoKeybd(const int baseOctave, const int numKeys, const int startKey, QWidget *parent) : QGraphicsView(parent), d(new PianoKeybdPrivate) { initialize(); initScene(baseOctave, numKeys, startKey); } /** * @brief Destructor */ PianoKeybd::~PianoKeybd() { d->m_scene->setRawKeyboardMode(false); setKeyboardMap(nullptr); } /** * Gets the PianoHandler pointer to the note receiver. * * If this method returns null, then there is not a PianoHandler class assigned, * and then the signals noteOn() and noteOff() are emitted instead. * @return pointer to the PianoHandler class, if there is one assigned */ PianoHandler *PianoKeybd::getPianoHandler() const { return d->m_scene->getPianoHandler(); } /** * Assigns a PianoHandler pointer for processing note events. * * When this member is used to assign a PianoHandler instance, then * the methods in that instance are called instead of emitting the * signals noteOn() and noteOff(). * @param handler pointer to the PianoHandler instance */ void PianoKeybd::setPianoHandler(PianoHandler *handler) { d->m_scene->setPianoHandler(handler); } /** * Returns the palette used for highlighting the played keys * @return The PianoPalette used to highlight the played keys */ PianoPalette PianoKeybd::getHighlightPalette() const { return d->m_scene->getHighlightPalette(); } /** * Assigns the palette used for highlighting the played keys. When the palette * has a single color, the method setKeyPressedColor() may be used instead. * @see setKeyPressedColor() * @param p PianoPalette const reference */ void PianoKeybd::setHighlightPalette(const PianoPalette& p) { d->m_scene->setHighlightPalette(p); } /** * Returns the palette used to paint the keys' background. * @return The PianoPalette used to paint the keys' background */ PianoPalette PianoKeybd::getBackgroundPalette() const { return d->m_scene->getBackgroundPalette(); } /** * Assigns the palette used to paint the keys' background. * @param p PianoPalette const reference */ void PianoKeybd::setBackgroundPalette(const PianoPalette& p) { d->m_scene->setBackgroundPalette(p); } /** * Returns the palette used to paint texts over the keys like the note names * or custom labels. * @return The PianoPalette used to paint the keys' foreground */ PianoPalette PianoKeybd::getForegroundPalette() const { return d->m_scene->getForegroundPalette(); } /** * Assigns the palette used to paint texts over the keys like the note names * or custom labels. * @param p PianoPalette const reference */ void PianoKeybd::setForegroundPalette(const PianoPalette &p) { d->m_scene->setForegroundPalette(p); } /** * Returns true if the color scale background palette is assigned and active. * @return whether the color scale display is enabled or not */ bool PianoKeybd::showColorScale() const { return d->m_scene->showColorScale(); } /** * Enables or disables the color scale background palette. * @param show the color scale activation state */ void PianoKeybd::setShowColorScale(const bool show) { d->m_scene->setShowColorScale(show); } /** * Assigns a list of custom text labels to be displayer over the keys. * * This can be used for instance, to display the names of the percussion sounds for * General MIDI channel 10. The number of elements should be 128, when * naming the whole set of MIDI notes, or at least 12 when naming the note names, * in which case an octave designation may be attached to the supplied name. * @param names list of key labels */ void PianoKeybd::useCustomNoteNames(const QStringList &names) { d->m_scene->useCustomNoteNames(names); } /** * Disables the custom note names usage as labels over the keys, and restores * the standard note names instead. */ void PianoKeybd::useStandardNoteNames() { d->m_scene->useStandardNoteNames(); } /** * Returns the list of custom note names. * @return the list of custom key names */ QStringList PianoKeybd::customNoteNames() const { return d->m_scene->customNoteNames(); } /** * Returns the list of standard note names. * @return the list of standard key names */ QStringList PianoKeybd::standardNoteNames() const { return d->m_scene->standardNoteNames(); } /** * Updates the standard names of notes according to the * currently active program language translation. * The custom note names are not affected. */ void PianoKeybd::retranslate() { d->m_scene->retranslate(); } /** * Creates and initializes a new PianoScene instance and assigns it to this widget. * @param base octave base number * @param num number of displayed keys * @param strt starting note number * @param c single default highlight color */ void PianoKeybd::initScene(int base, int num, int strt, const QColor& c) { d->m_scene = new PianoScene(base, num, strt, c, this); d->m_scene->setKeyboardMap(&g_DefaultKeyMap); connect(d->m_scene, &PianoScene::noteOn, this, &PianoKeybd::noteOn); connect(d->m_scene, &PianoScene::noteOff, this, &PianoKeybd::noteOff); connect(d->m_scene, &PianoScene::signalName, this, &PianoKeybd::signalName); setScene(d->m_scene); } /** * This method is called from the available constructors * to initialize some widget attributes, settings, and optimizations. */ void PianoKeybd::initialize() { setAttribute(Qt::WA_AcceptTouchEvents); setAttribute(Qt::WA_InputMethodEnabled, false); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setViewportUpdateMode(MinimalViewportUpdate); setRenderHints(QPainter::Antialiasing|QPainter::TextAntialiasing|QPainter::SmoothPixmapTransform); #if (QT_VERSION < QT_VERSION_CHECK(5,15,0)) setOptimizationFlag(DontClipPainter, true); #endif setOptimizationFlag(DontSavePainterState, true); setOptimizationFlag(DontAdjustForAntialiasing, true); initResources(); } /** * This method overrides QGraphicsView::resizeEvent() * to keep the aspect ratio of the keys scene when the view is resized. * @param event */ void PianoKeybd::resizeEvent(QResizeEvent *event) { QGraphicsView::resizeEvent(event); fitInView(d->m_scene->sceneRect(), Qt::KeepAspectRatio); } /** * This method overrides QGraphicsView::viewportEvent() * Only touchscreen events are processed here. * @param ev The viewport event * @return true if the event has been consumed, false otherwise */ bool PianoKeybd::viewportEvent(QEvent *ev) { #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) static const auto touchScreen = QTouchDevice::DeviceType::TouchScreen; #else static const auto touchScreen = QInputDevice::DeviceType::TouchScreen; #endif switch(ev->type()) { case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: case QEvent::TouchCancel: { //qDebug() << Q_FUNC_INFO << ev->type(); QTouchEvent *touchEvent = static_cast(ev); if (isTouchEnabled() && (touchEvent->device()->type() == touchScreen)) { return d->m_scene->touchScreenEvent(touchEvent); } break; } default: break; } return QGraphicsView::viewportEvent(ev); } /** * This method changes the number of displayed keys * and the starting key number, keeping the other settings the same. * The common industrial piano layout has 88 keys, and starts with A, so * the default starting key value is 9. But any natural note/key * number is possible as starting note key. * The key/note values are: C=0, D=2, E=4, F=5, G=7, A=9, B=11. * @see numKeys(), startKey(), setStartKey() * @param numKeys The new number of keys * @param startKey The number of the starting key */ void PianoKeybd::setNumKeys(const int numKeys, const int startKey) { //qDebug() << Q_FUNC_INFO << numKeys << startKey; if ( numKeys != d->m_scene->numKeys() || startKey != d->m_scene->startKey() ) { QByteArray dataBuffer; int baseOctave = d->m_scene->baseOctave(); QColor color = d->m_scene->getKeyPressedColor(); PianoHandler* handler = d->m_scene->getPianoHandler(); KeyboardMap* keyMap = d->m_scene->getKeyboardMap(); d->m_scene->saveData(dataBuffer); delete d->m_scene; initScene(baseOctave, numKeys, startKey, color); d->m_scene->loadData(dataBuffer); d->m_scene->setPianoHandler(handler); d->m_scene->setKeyboardMap(keyMap); d->m_scene->hideOrShowKeys(); d->m_scene->refreshKeys(); d->m_scene->refreshLabels(); fitInView(d->m_scene->sceneRect(), Qt::KeepAspectRatio); } } /** * Rotates the keyboard view an angle clockwise. * @param r rotating angle in degrees to rotate. */ void PianoKeybd::setRotation(int r) { if (r != d->m_rotation) { d->m_rotation = r; resetTransform(); rotate(d->m_rotation); fitInView(d->m_scene->sceneRect(), Qt::KeepAspectRatio); } } /** * Overrides QGraphicsView::sizeHint() providing a size value based on the piano scene. * @return The sizeHint property of the piano view */ QSize PianoKeybd::sizeHint() const { return mapFromScene(sceneRect()).boundingRect().size(); } // RAWKBD_SUPPORT bool PianoKeybd::handleKeyPressed(int keycode) { if (d->m_scene->isKeyboardEnabled() && d->m_rawMap != nullptr && d->m_rawMap->contains(keycode)) { d->m_scene->keyOn(d->m_rawMap->value(keycode)); return true; } return false; } bool PianoKeybd::handleKeyReleased(int keycode) { if (d->m_scene->isKeyboardEnabled() && d->m_rawMap != nullptr && d->m_rawMap->contains(keycode)) { d->m_scene->keyOff(d->m_rawMap->value(keycode)); return true; } return false; } /** * @brief Assigns a custom picture to the white or black keys * that will be used as a texture to paint the keys. * @param natural true for white keys, false for black keys * @param pix this transparent QPixmap will be used to paint the keys */ void PianoKeybd::setKeyPicture(const bool natural, const QPixmap &pix) { d->m_scene->setKeyPicture(natural, pix); } /** * @brief Returns the custom picture used to paint the corresponding keys. * @param natural true for white keys, false for black keys * @return a transparent QPixmap used to paint the keys */ QPixmap PianoKeybd::getKeyPicture(const bool natural) { return d->m_scene->getKeyPicture(natural); } /** * @brief Enables or disables a picture to paint the keys. * @param enable or disable */ void PianoKeybd::setUseKeyPictures(const bool enable) { d->m_scene->setUseKeyPictures(enable); } /** * @brief Returns whether pictures are used to paint the keys. * @return true if the pictures are enabled to paint the keys */ bool PianoKeybd::getUseKeyPictures() const { return d->m_scene->getUseKeyPictures(); } /** * @brief Enables or disables the application level usage of a native event filter * * The native event filter should process low level keyboard events, * calling the methods of the @ref RawKbdHandler interface. This method should be * used in this case to indicate that the keyboard events should be ignored * by the piano scene keyboard event handlers. * Note: this is only necessary if the native filter does not block events. * @param newState of the application level usage of a native event filter */ void PianoKeybd::setUsingNativeFilter(const bool newState) { d->m_scene->setUsingNativeFilter( newState ); } /** * @brief Returns whether the application is filtering native events * * The native event filter should process low level keyboard events, * calling the methods of the @ref RawKbdHandler interface. * @return true if the application is filtering native events */ bool PianoKeybd::isUsingNativeFilter() const { return d->m_scene->isUsingNativeFilter(); } /** * @brief Enables or disables the octave subscript designation * * According to the Scientific pitch notation (SPN), also known as * American standard pitch notation (ASPN), the octave designation * should be written as a subscript, but it is an user choice. * * @see octaveSubscript() * @param enable or disable using subscript octave numbers * @since 2.7.0 */ void PianoKeybd::setOctaveSubscript(const bool enable) { d->m_scene->setOctaveSubscript( enable ); } /** * @brief Returns whether the octave subscript designation is enabled * * According to the Scientific pitch notation (SPN), also known as * American standard pitch notation (ASPN), the octave designation * should be written as a subscript. * @see setOctaveSubscript() * @return true if the octave subscript designation is enabled * @since 2.7.0 */ bool PianoKeybd::octaveSubscript() const { return d->m_scene->octaveSubscript(); } /** * @brief Sets the initial/starting note key * * The common industrial piano keys layout (88 keys) starts with A, so * the default starting key value is 9. But any natural note/key * number is possible as starting note key. * The key/note values are: C=0, D=2, E=4, F=5, G=7, A=9, B=11. * @see setNumKeys() * @since 2.7.0 * @param startKey is a note/key number between 0 and 11 */ void PianoKeybd::setStartKey(const int startKey) { setNumKeys(numKeys(), startKey); } /** * Returns the base octave number. * @see setBaseOctave() * @return the base octave number */ int PianoKeybd::baseOctave() const { return d->m_scene->baseOctave(); } /** * Assigns the base octave number. * @param baseOctave the base octave number */ void PianoKeybd::setBaseOctave(const int baseOctave) { d->m_scene->setBaseOctave(baseOctave); } /** * Returns the total number of keys * @see setNumKeys() * @return the number of keys displayed */ int PianoKeybd::numKeys() const { return d->m_scene->numKeys(); } /** * Returns the starting key note: C=0, A=9 and so on. * @return the starting key note. */ int PianoKeybd::startKey() const { return d->m_scene->startKey(); } /** * Returns the rotation angle in degrees, clockwise, of the piano view. * @return the rotation angle in degrees. */ int PianoKeybd::getRotation() const { return d->m_rotation; } /** * Returns the key highlight color. * @return the key highlight color */ QColor PianoKeybd::getKeyPressedColor() const { return d->m_scene->getKeyPressedColor(); } /** * Assigns a single color for key highlight. This is an alternative to creating a * highlight palette with a single color and assigning it. * @see setHighlightPalette() * @param c color for key highlight */ void PianoKeybd::setKeyPressedColor(const QColor& c) { d->m_scene->setKeyPressedColor(c); } /** * Assigns the default highlight palette colors and assigns it to the scene. */ void PianoKeybd::resetKeyPressedColor() { d->m_scene->resetKeyPressedColor(); } /** * Returns the label visibility policy. * @see setShowLabels() * @return the label visibility policy */ LabelVisibility PianoKeybd::showLabels() const { return d->m_scene->showLabels(); } /** * Assigns the label visibility policy. * @see LabelVisibility * @param show the label visibility policy */ void PianoKeybd::setShowLabels(const LabelVisibility show) { d->m_scene->setShowLabels(show); } /** * Returns the label alterations policy. * @see setLabelAlterations() * @return the label alterations policy */ LabelAlteration PianoKeybd::labelAlterations() const { return d->m_scene->alterations(); } /** * Assigns the label alterations policy. * @see LabelAlteration * @param use the label alterations policy */ void PianoKeybd::setLabelAlterations(const LabelAlteration use) { d->m_scene->setAlterations(use); } /** * Returns the labels orientation policy. * @see setLabelOrientation() * @return the labels orientation policy */ LabelOrientation PianoKeybd::labelOrientation() const { return d->m_scene->getOrientation(); } /** * Assigns the labels orientation policy. * @see LabelOrientation * @param orientation the labels orientation policy */ void PianoKeybd::setLabelOrientation(const LabelOrientation orientation) { d->m_scene->setOrientation(orientation); } /** * Returns the octave label policy. * @see setLabelOctave() * @return the octave label policy */ LabelCentralOctave PianoKeybd::labelOctave() const { return d->m_scene->getOctave(); } /** * Assigns the octave label policy * @see LabelCentralOctave * @param octave the octave label policy */ void PianoKeybd::setLabelOctave(const LabelCentralOctave octave) { d->m_scene->setOctave(octave); } /** * Returns the transpose amount in semitones. * @see setTranspose() * @return the transpose amount in semitones */ int PianoKeybd::getTranspose() const { return d->m_scene->getTranspose(); } /** * Assigns the transpose amount in semitones. * @see getTranspose() * @param t the transpose amount in semitones */ void PianoKeybd::setTranspose(int t) { d->m_scene->setTranspose(t); } /** * Returns the MIDI Channel (0-15). * @return the MIDI Channel (0-15) */ int PianoKeybd::getChannel() const { return d->m_scene->getChannel(); } /** * Assigns the MIDI Channel (0-15). * @param c the MIDI Channel (0-15) */ void PianoKeybd::setChannel(const int c) { d->m_scene->setChannel(c); } /** * Returns the MIDI note velocity * @see setVelocity() * @return the MIDI note velocity */ int PianoKeybd::getVelocity() const { return d->m_scene->getVelocity(); } /** * Assigns the MIDI note velocity. * @see getVelocity() * @param v the MIDI note velocity */ void PianoKeybd::setVelocity(const int v) { d->m_scene->setVelocity(v); } /** * Returns whether the computer keyboard is enabled. * @return true if the computer keyboard is enabled. */ bool PianoKeybd::isKeyboardEnabled() const { return d->m_scene->isKeyboardEnabled(); } /** * Enables or disables the computer keyboard note input. * @param enable the computer keyboard note input. */ void PianoKeybd::setKeyboardEnabled(const bool enable) { d->m_scene->setKeyboardEnabled(enable); } /** * Returns whether the mouse note input is enabled. * @return true if the mouse note input is enabled */ bool PianoKeybd::isMouseEnabled() const { return d->m_scene->isMouseEnabled(); } /** * Enables or disables the mouse note input. * @param enable the mouse note input */ void PianoKeybd::setMouseEnabled(const bool enable) { d->m_scene->setMouseEnabled(enable); } /** * Returns whether the touch screen note input is enabled. * @return true if the touch screen note input is enabled */ bool PianoKeybd::isTouchEnabled() const { return d->m_scene->isTouchEnabled(); } /** * Enables or disables the touch screen note input. * @param enable the touch screen note input. */ void PianoKeybd::setTouchEnabled(const bool enable) { d->m_scene->setTouchEnabled(enable); } /** * Returns whether the note MIDI velocity influences the highlight color tint. * @return true if the note MIDI velocity influences the highlight color tint */ bool PianoKeybd::velocityTint() const { return d->m_scene->velocityTint(); } /** * Enables or disables the note MIDI velocity influencing the highlight color tint. * @param enable the note MIDI velocity influencing the highlight color tint */ void PianoKeybd::setVelocityTint(const bool enable) { //qDebug() << Q_FUNC_INFO << enable; d->m_scene->setVelocityTint(enable); } /** * Forces all active notes to silence. */ void PianoKeybd::allKeysOff() { d->m_scene->allKeysOff(); } /** * Assigns the computer keyboard note map. * @param m the computer keyboard note map. */ void PianoKeybd::setKeyboardMap(KeyboardMap* m) { d->m_scene->setKeyboardMap(m); } /** * Returns the computer keyboard note map. * @return the computer keyboard note map */ KeyboardMap* PianoKeybd::getKeyboardMap() { return d->m_scene->getKeyboardMap(); } /** * Resets the computer keyboard note map to the default one. */ void PianoKeybd::resetRawKeyboardMap() { d->m_rawMap = &g_DefaultRawKeyMap; d->m_scene->setKeyboardMap(&g_DefaultRawKeyMap); } /** * Returns the low level computer keyboard note map. * @return the low level computer keyboard note map */ bool PianoKeybd::getRawKeyboardMode() const { return d->m_scene->getRawKeyboardMode(); } /** * Enables or disables the low level computer keyboard mode. * @param b the low level computer keyboard mode */ void PianoKeybd::setRawKeyboardMode(const bool b) { d->m_scene->setRawKeyboardMode(b); } /** * Resets the low level computer keyboard note map to the default one. */ void PianoKeybd::resetKeyboardMap() { d->m_scene->setKeyboardMap(&g_DefaultKeyMap); } /** * Assigns the low level computer keyboard note map. * @param m the low level computer keyboard note map */ void PianoKeybd::setRawKeyboardMap(KeyboardMap *m) { d->m_rawMap = m; d->m_scene->setKeyboardMap(m); } /** * Returns the low level computer keyboard note map. * @return the low level computer keyboard note map */ KeyboardMap *PianoKeybd::getRawKeyboardMap() { return d->m_rawMap; } /** * Highlights one note key with the specified color and velocity * @param note The MIDI note number * @param color The highlight color * @param vel The MIDI note velocity */ void PianoKeybd::showNoteOn(const int note, QColor color, int vel) { d->m_scene->showNoteOn(note, color, vel); } /** * Highlights one note key with the specified velocity * @param note The MIDI note number * @param vel The MIDI note velocity */ void PianoKeybd::showNoteOn(const int note, int vel) { d->m_scene->showNoteOn(note, vel); } /** * Shows inactive one note key with the specified velocity * @param note The MIDI note number * @param vel The MIDI note velocity */ void PianoKeybd::showNoteOff(const int note, int vel) { d->m_scene->showNoteOff(note, vel); } /** * Assigns a typographic font for drawing the note labels over the piano keys. * @param font typographic font for drawing the note labels */ void PianoKeybd::setFont(const QFont &font) { QWidget::setFont(font); d->m_scene->setFont(font); d->m_scene->refreshLabels(); } } // namespace widgets } // namespace drumstick drumstick-2.9.0/library/widgets/sonivoxsettingsdialog.cpp0000644000175000017500000001776014541630232023032 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include "sonivoxsettingsdialog.h" #include "ui_sonivoxsettingsdialog.h" #include #include /** * @file sonivoxsettingsdialog.cpp * Implementation of the Sonivox Synth configuration dialog */ namespace drumstick { namespace widgets { const QString SonivoxSettingsDialog::QSTR_PREFERENCES = QStringLiteral("SonivoxEAS"); const QString SonivoxSettingsDialog::QSTR_BUFFERTIME = QStringLiteral("BufferTime"); const QString SonivoxSettingsDialog::QSTR_REVERBTYPE = QStringLiteral("ReverbType"); const QString SonivoxSettingsDialog::QSTR_REVERBAMT = QStringLiteral("ReverbAmt"); const QString SonivoxSettingsDialog::QSTR_CHORUSTYPE = QStringLiteral("ChorusType"); const QString SonivoxSettingsDialog::QSTR_CHORUSAMT = QStringLiteral("ChorusAmt"); const QString SonivoxSettingsDialog::QSTR_SOUNDFONT = QStringLiteral("InstrumentsDefinition"); const QString SonivoxSettingsDialog::QSTR_DATADIR = QStringLiteral("soundfonts"); const QString SonivoxSettingsDialog::QSTR_DATADIR2 = QStringLiteral("sounds/sf2"); SonivoxSettingsDialog::SonivoxSettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::SonivoxSettingsDialog) { ui->setupUi(this); ui->combo_Reverb->addItem(QStringLiteral("Large Hall"), 0); ui->combo_Reverb->addItem(QStringLiteral("Hall"), 1); ui->combo_Reverb->addItem(QStringLiteral("Chamber"), 2); ui->combo_Reverb->addItem(QStringLiteral("Room"), 3); ui->combo_Reverb->addItem(QStringLiteral("None"), -1); ui->combo_Reverb->setCurrentIndex(4); ui->combo_Chorus->addItem(QStringLiteral("Preset 1"), 0); ui->combo_Chorus->addItem(QStringLiteral("Preset 2"), 1); ui->combo_Chorus->addItem(QStringLiteral("Preset 3"), 2); ui->combo_Chorus->addItem(QStringLiteral("Preset 4"), 3); ui->combo_Chorus->addItem(QStringLiteral("None"), -1); ui->combo_Chorus->setCurrentIndex(4); connect(ui->btn_soundfont, &QToolButton::clicked, this, &SonivoxSettingsDialog::showFileDialog); connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::pressed, this, &SonivoxSettingsDialog::restoreDefaults); drumstick::rt::BackendManager man; m_driver = man.outputBackendByName("SonivoxEAS"); //qDebug() << Q_FUNC_INFO; } SonivoxSettingsDialog::~SonivoxSettingsDialog() { //qDebug() << Q_FUNC_INFO; if (m_driver != nullptr) { m_driver->close(); } delete ui; } void SonivoxSettingsDialog::accept() { //qDebug() << Q_FUNC_INFO; writeSettings(); if (m_driver != nullptr) { QString title; QVariant varStatus = m_driver->property("status"); if (varStatus.isValid()) { title = varStatus.toBool() ? tr("Sonivox Initialized") : tr("Sonivox Initialization Failed"); QVariant varDiag = m_driver->property("diagnostics"); if (varDiag.isValid()) { QString text = varDiag.toStringList().join(QChar::LineFeed).trimmed(); if (varStatus.toBool()) { if (!text.isEmpty()) { QMessageBox::information(this, title, text); } } else { QMessageBox::critical(this, title, text); return; } } } } QDialog::accept(); } void SonivoxSettingsDialog::showEvent(QShowEvent *event) { //qDebug() << Q_FUNC_INFO; readSettings(); event->accept(); } void SonivoxSettingsDialog::readSettings() { //qDebug() << Q_FUNC_INFO; SettingsFactory settings; settings->beginGroup(QSTR_PREFERENCES); int bufferTime = settings->value(QSTR_BUFFERTIME, 30).toInt(); int reverbType = settings->value(QSTR_REVERBTYPE, 1).toInt(); int reverbAmt = settings->value(QSTR_REVERBAMT, 25800).toInt(); int chorusType = settings->value(QSTR_CHORUSTYPE, -1).toInt(); int chorusAmt = settings->value(QSTR_CHORUSAMT, 0).toInt(); QString soundfont = settings->value(QSTR_SOUNDFONT, QString()).toString(); settings->endGroup(); if (qEnvironmentVariableIsSet("PULSE_LATENCY_MSEC")) { bufferTime = qEnvironmentVariableIntValue("PULSE_LATENCY_MSEC"); } ui->spnTime->setValue(bufferTime); ui->soundfont_dls->setText(soundfont); ui->dial_Reverb->setValue(reverbAmt); ui->dial_Chorus->setValue(chorusAmt); int reverbIndex = ui->combo_Reverb->findData(reverbType); int chorusIndex = ui->combo_Chorus->findData(chorusType); ui->combo_Reverb->setCurrentIndex(reverbIndex); ui->combo_Chorus->setCurrentIndex(chorusIndex); chkDriverProperties(settings.getQSettings()); } void SonivoxSettingsDialog::writeSettings() { //qDebug() << Q_FUNC_INFO; SettingsFactory settings; settings->beginGroup(QSTR_PREFERENCES); settings->setValue(QSTR_BUFFERTIME, ui->spnTime->value()); settings->setValue(QSTR_REVERBTYPE, ui->combo_Reverb->currentData()); settings->setValue(QSTR_CHORUSTYPE, ui->combo_Chorus->currentData()); settings->setValue(QSTR_REVERBAMT, ui->dial_Reverb->value()); settings->setValue(QSTR_CHORUSAMT, ui->dial_Chorus->value()); settings->setValue(QSTR_SOUNDFONT, ui->soundfont_dls->text()); settings->endGroup(); settings->sync(); qputenv("PULSE_LATENCY_MSEC", QByteArray::number( ui->spnTime->value() )); chkDriverProperties(settings.getQSettings()); } void SonivoxSettingsDialog::chkDriverProperties(QSettings *settings) { //qDebug() << Q_FUNC_INFO; if (m_driver != nullptr) { //drumstick::rt::MIDIConnection conn; m_driver->close(); m_driver->initialize(settings); QVariant varVersion = m_driver->property("libversion"); if (varVersion.isValid()) { ui->lblLibraryText->clear(); ui->lblLibraryText->setText(varVersion.toString()); } QVariant varStatus = m_driver->property("status"); if (varStatus.isValid()) { ui->lblStatusText->clear(); ui->lblStatusText->setText(varStatus.toBool() ? tr("Ready") : tr("Failed") ); ui->lblStatusIcon->setPixmap(varStatus.toBool() ? QPixmap(":/checked.png") : QPixmap(":/error.png") ); } } } void SonivoxSettingsDialog::restoreDefaults() { ui->spnTime->setValue(30); ui->soundfont_dls->clear(); ui->combo_Reverb->setCurrentIndex(1); ui->dial_Reverb->setValue(25800); ui->combo_Chorus->setCurrentIndex(4); ui->dial_Chorus->setValue(0); } void SonivoxSettingsDialog::showFileDialog() { QDir dir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR, QStandardPaths::LocateDirectory)); if (!dir.exists()) { dir = QDir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR2, QStandardPaths::LocateDirectory)); } QString fileName = QFileDialog::getOpenFileName(this, tr("Select SoundFont"), dir.absolutePath(), tr("SoundFont Files (*.dls)")); if (!fileName.isEmpty()) { ui->soundfont_dls->setText(fileName); } } void SonivoxSettingsDialog::changeSoundFont(const QString& fileName) { readSettings(); ui->soundfont_dls->setText(fileName); writeSettings(); } } // namespace widgets } // namespace drumstick drumstick-2.9.0/library/widgets/sonivoxsettingsdialog.ui0000644000175000017500000001710714541630232022660 0ustar pedropedro drumstick::widgets::SonivoxSettingsDialog 0 0 320 360 320 360 Sonivox EAS Synth :/icon.png:/icon.png SoundFont: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter soundfont_dls true 0 0 ... 0 0 Sonivox Library Version: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 Reverb Qt::AlignCenter Chorus Qt::AlignCenter 32765 Qt::Vertical 20 90 32765 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults 0 0 Init. Status: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 0 0 0 0 Buffer Time: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter ms 10 200 30 spnTime dial_Reverb dial_Chorus combo_Reverb combo_Chorus buttonBox accepted() drumstick::widgets::SonivoxSettingsDialog accept() 145 260 148 288 buttonBox rejected() drumstick::widgets::SonivoxSettingsDialog reject() 239 260 251 289 drumstick-2.9.0/library/widgets/widgets.pro0000644000175000017500000000514614541630232020043 0ustar pedropedroTEMPLATE = lib TARGET = drumstick-widgets DESTDIR = ../../build/lib DEPENDPATH += . ../include INCLUDEPATH += . ../include include (../../global.pri) CONFIG += c++11 qt create_pc create_prl no_install_prl lrelease static { CONFIG += staticlib } LRELEASE_DIR=. DEFINES += drumstick_widgets_EXPORTS ENABLE_NETWORK QMAKE_CXXFLAGS += $$QMAKE_CXXFLAGS_HIDESYMS QMAKE_PKGCONFIG_PREFIX = $$INSTALLBASE QT += widgets network LIBS += -L$$OUT_PWD/../../build/lib -ldrumstick-rt FORMS += \ fluidsettingsdialog.ui \ networksettingsdialog.ui HEADERS += \ ../include/drumstick/pianokeybd.h \ ../include/drumstick/pianopalette.h \ ../include/drumstick/rtmidiinput.h \ ../include/drumstick/rtmidioutput.h \ ../include/drumstick/configurationdialogs.h \ ../include/drumstick/settingsfactory.h \ pianoscene.h \ pianokey.h \ keylabel.h \ fluidsettingsdialog.h \ networksettingsdialog.h SOURCES += \ configurationdialogs.cpp \ pianokey.cpp \ pianokeybd.cpp \ pianoscene.cpp \ pianopalette.cpp \ keylabel.cpp \ fluidsettingsdialog.cpp \ networksettingsdialog.cpp \ settingsfactory.cpp RESOURCES += pianokeybd.qrc TRANSLATIONS += \ translations/drumstick-widgets_en.ts \ translations/drumstick-widgets_cs.ts \ translations/drumstick-widgets_de.ts \ translations/drumstick-widgets_es.ts \ translations/drumstick-widgets_fr.ts \ translations/drumstick-widgets_gl.ts \ translations/drumstick-widgets_it.ts \ translations/drumstick-widgets_nl.ts \ translations/drumstick-widgets_ru.ts \ translations/drumstick-widgets_sr.ts \ translations/drumstick-widgets_sv.ts \ translations/drumstick-widgets_tr.ts \ translations/drumstick-widgets_zh_CN.ts macx { FORMS += macsynthsettingsdialog.ui HEADERS += macsynthsettingsdialog.h SOURCES += macsynthsettingsdialog.cpp } linux { FORMS += sonivoxsettingsdialog.ui HEADERS += sonivoxsettingsdialog.h SOURCES += sonivoxsettingsdialog.cpp } macx:!static { TARGET = drumstick-widgets CONFIG += lib_bundle FRAMEWORK_HEADERS.version = Versions FRAMEWORK_HEADERS.files = $$HEADERS FRAMEWORK_HEADERS.path = Headers/drumstick QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS #QMAKE_LFLAGS_SONAME = -Wl,-install_name,@executable_path/../Frameworks/ QMAKE_SONAME_PREFIX = @rpath QMAKE_TARGET_BUNDLE_PREFIX = net.sourceforge QMAKE_BUNDLE = drumstick-widgets QMAKE_INFO_PLIST = ../Info.plist.lib } packagesExist(fluidsynth) { DEFINES += ENABLE_FLUIDSYNTH } packagesExist(libpulse-simple):packagesExist(sonivox) { DEFINES += ENABLE_SONIVOX } drumstick-2.9.0/library/widgets/configurationdialogs.cpp0000644000175000017500000001703114541630232022565 0ustar pedropedro/* Drumstick MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #if defined(ENABLE_FLUIDSYNTH) #include "fluidsettingsdialog.h" #endif #if defined(ENABLE_NETWORK) #include "networksettingsdialog.h" #endif #if defined(ENABLE_SONIVOX) #include "sonivoxsettingsdialog.h" #endif #if defined(Q_OS_MACOS) #include "macsynthsettingsdialog.h" #endif /** * @file configurationdialogs.cpp * Implementation of the configuration dialogs */ /** * @addtogroup Widgets Drumstick Widgets * @{ */ namespace drumstick { namespace widgets { /** * @brief inputDriverIsConfigurable * @param driver the driver name * @return true if the input driver has a configuration dialog */ bool inputDriverIsConfigurable(const QString driver) { // internal configuration dialogs: if (driver == "Network") { return true; } // external configuration dialogs (residing on plugins): drumstick::rt::BackendManager man; auto obj = man.inputBackendByName(driver); if (obj == nullptr) { return false; } auto metaObj = obj->metaObject(); if ((metaObj->indexOfProperty("isconfigurable") != -1) && (metaObj->indexOfMethod("configure(QWidget*)") != -1)) { auto configurable = obj->property("isconfigurable"); if (configurable.isValid()) { return configurable.toBool(); } } return false; } /** * @brief outputDriverIsConfigurable * @param driver the driver name * @return true if the output driver has a configuration dialog */ bool outputDriverIsConfigurable(const QString driver) { // internal configuration dialogs if ((driver == "Network") #if defined(ENABLE_SONIVOX) || (driver == "SonivoxEAS") #endif #if defined(Q_OS_MACOS) || (driver == "DLS Synth") #endif #if defined(ENABLE_FLUIDSYNTH) || (driver == "FluidSynth") #endif ) { return true; } // external configuration dialogs (residing on plugins) drumstick::rt::BackendManager man; auto obj = man.outputBackendByName(driver); if (obj == nullptr) { return false; } auto metaObj = obj->metaObject(); if ((metaObj->indexOfProperty("isconfigurable") != -1) && (metaObj->indexOfMethod("configure(QWidget*)") != -1)) { auto configurable = obj->property("isconfigurable"); if (configurable.isValid()) { return configurable.toBool(); } } return false; } /** * @brief Input Driver configuration dialog * Some RT input drivers can be configured. This function provides a * dialog box to show and edit the configurable parameters, and * save the settings. Curremtly, only the Network driver is configurable. * @param driver name of the driver * @param parent optional parent widget * @return true if configuration has changed */ bool configureInputDriver(const QString driver, QWidget* parent) { // internal configuration dialogs #if defined(ENABLE_NETWORK) if (driver == "Network") { NetworkSettingsDialog dlg(true, parent); return (dlg.exec() == QDialog::Accepted); } #endif // external configuration dialogs (residing on plugins): drumstick::rt::BackendManager man; auto obj = man.inputBackendByName(driver); if (obj == nullptr) { return false; } auto metaObj = obj->metaObject(); if ((metaObj->indexOfProperty("isconfigurable") != -1) && (metaObj->indexOfMethod("configure(QWidget*)") != -1)) { auto configurable = obj->property("isconfigurable"); if (configurable.isValid() && configurable.toBool()) { bool ret{false}; QMetaObject::invokeMethod(obj, "configure", Q_RETURN_ARG(bool, ret), Q_ARG(QWidget*, parent)); return ret; } } return false; } /** * @brief Output Driver configuration dialog * Some RT output drivers can be configured. This function provides a * dialog box to show and edit the configurable parameters, and * save the settings. Curremtly the Network, FluidSynth, SonivoxEAS, * and macOS DLS Synth drivers are configurable. * @param driver name of the driver * @param parent optional parent widget * @return true if configuration has changed */ bool configureOutputDriver(const QString driver, QWidget* parent) { // internal configuration dialogs #if defined(ENABLE_NETWORK) if (driver == "Network") { NetworkSettingsDialog dlg(false, parent); return (dlg.exec() == QDialog::Accepted); } #endif #if defined(ENABLE_FLUIDSYNTH) if (driver == "FluidSynth") { FluidSettingsDialog dlg(parent); return (dlg.exec() == QDialog::Accepted); } #endif #if defined(ENABLE_SONIVOX) if (driver == "SonivoxEAS") { SonivoxSettingsDialog dlg(parent); return (dlg.exec() == QDialog::Accepted); } #endif #if defined(Q_OS_MACOS) if (driver == "DLS Synth") { MacSynthSettingsDialog dlg(parent); return (dlg.exec() == QDialog::Accepted); } #endif // external configuration dialogs (residing on plugins): drumstick::rt::BackendManager man; auto obj = man.outputBackendByName(driver); if (obj == nullptr) { return false; } auto metaObj = obj->metaObject(); if ((metaObj->indexOfProperty("isconfigurable") != -1) && (metaObj->indexOfMethod("configure(QWidget*)") != -1)) { auto configurable = obj->property("isconfigurable"); if (configurable.isValid() && configurable.toBool()) { bool ret{true}; QMetaObject::invokeMethod(obj, "configure", Q_RETURN_ARG(bool, ret), Q_ARG(QWidget*, parent)); return ret; } } return false; } /** * @brief Changes the sound font configuration * Some RT output drivers accept soundfonts. This function allows to * change the soundfont file for a driver and store the setting. The * FluidSynth and macOS DLS Synth drivers are currently supported. * @param driver name of the driver * @param fileName name of the soundfont file * @param parent optional parent widget * @return true if configuration has changed */ void changeSoundFont(const QString driver, const QString fileName, QWidget* parent) { #if defined(ENABLE_FLUIDSYNTH) if (driver == "FluidSynth") { FluidSettingsDialog dlg(parent); dlg.changeSoundFont(fileName); } #endif #if defined(ENABLE_SONIVOX) if (driver == "SonivoxEAS") { SonivoxSettingsDialog dlg(parent); dlg.changeSoundFont(fileName); } #endif #if defined(Q_OS_MACOS) if (driver == "DLS Synth") { MacSynthSettingsDialog dlg(parent); dlg.changeSoundFont(fileName); } #endif } /** * @brief libraryVersion returns the runtime library version as a QString * @return version string */ QString libraryVersion() { return QStringLiteral(QT_STRINGIFY(VERSION)); } } // namespace widgets } // namespace drumstick /** @} */ drumstick-2.9.0/library/widgets/CMakeLists.txt0000644000175000017500000001707014541630232020412 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(CMAKE_INCLUDE_CURRENT_DIR ON) find_package(Qt${QT_VERSION_MAJOR}Widgets REQUIRED) find_package(Qt${QT_VERSION_MAJOR}LinguistTools REQUIRED) set(drumstick-widgets_HEADERS ../include/drumstick/pianokeybd.h ../include/drumstick/pianopalette.h ../include/drumstick/settingsfactory.h ../include/drumstick/configurationdialogs.h ) set(drumstick-widgets_OBJ_SRCS ../include/drumstick/pianokeybd.h ../include/drumstick/pianopalette.h pianoscene.h ) if(BUILD_FRAMEWORKS) set_source_files_properties(${drumstick-widgets_HEADERS} PROPERTIES MACOSX_PACKAGE_LOCATION Headers/drumstick ) endif() set(drumstick-widgets_SRCS configurationdialogs.cpp keylabel.cpp keylabel.h pianokey.cpp pianokey.h pianokeybd.cpp pianopalette.cpp pianoscene.cpp pianoscene.h settingsfactory.cpp ) if (HAVE_FLUIDSYNTH) list( APPEND drumstick-widgets_OBJ_SRCS fluidsettingsdialog.h ) list( APPEND drumstick-widgets_SRCS fluidsettingsdialog.cpp fluidsettingsdialog.h ) list( APPEND drumstick-widgets_FORMS fluidsettingsdialog.ui ) endif() if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") list( APPEND drumstick-widgets_OBJ_SRCS macsynthsettingsdialog.h ) list( APPEND drumstick-widgets_SRCS macsynthsettingsdialog.cpp macsynthsettingsdialog.h ) list( APPEND drumstick-widgets_FORMS macsynthsettingsdialog.ui ) endif() if (HAVE_NETWORK) list( APPEND drumstick-widgets_OBJ_SRCS networksettingsdialog.h ) list( APPEND drumstick-widgets_SRCS networksettingsdialog.cpp networksettingsdialog.h ) list( APPEND drumstick-widgets_FORMS networksettingsdialog.ui ) endif() if (HAVE_SONIVOX) list( APPEND drumstick-widgets_OBJ_SRCS sonivoxsettingsdialog.h ) list( APPEND drumstick-widgets_SRCS sonivoxsettingsdialog.cpp sonivoxsettingsdialog.h ) list( APPEND drumstick-widgets_FORMS sonivoxsettingsdialog.ui ) endif() if (WIN32) set(TARGET_DESCRIPTION ${Drumstick_DESCRIPTION}) set(TARGET_NAME drumstick-widgets) set(TARGET_ORIGINAL_FILENAME libdrumstick-widgets.dll) configure_file(${Drumstick_SOURCE_DIR}/versioninfo.rc.in versioninfo.rc @ONLY) list(APPEND drumstick-widgets_SRCS versioninfo.rc) endif() if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_ui(drumstick-widgets_UI_SRCS ${drumstick-widgets_FORMS}) qt5_wrap_cpp(drumstick-widgets_MOC_SRCS ${drumstick-widgets_OBJ_SRCS}) qt5_add_resources(drumstick-widgets_RESOURCES pianokeybd.qrc) else() qt_wrap_ui(drumstick-widgets_UI_SRCS ${drumstick-widgets_FORMS}) qt_wrap_cpp(drumstick-widgets_MOC_SRCS ${drumstick-widgets_OBJ_SRCS}) qt_add_resources(drumstick-widgets_RESOURCES pianokeybd.qrc) endif() add_library(drumstick-widgets ${drumstick-widgets_UI_SRCS} ${drumstick-widgets_MOC_SRCS} ${drumstick-widgets_SRCS} ${drumstick-widgets_RESOURCES} ${drumstick-widgets_HEADERS} ) target_compile_definitions(drumstick-widgets PRIVATE $<$:ENABLE_NETWORK> $<$:ENABLE_FLUIDSYNTH> $<$:ENABLE_SONIVOX> QT_NO_SIGNALS_SLOTS_KEYWORDS ) add_library(Drumstick::Widgets ALIAS drumstick-widgets) target_include_directories(drumstick-widgets PUBLIC $ $ ) target_link_libraries(drumstick-widgets PRIVATE Qt${QT_VERSION_MAJOR}::Widgets $<$:Qt${QT_VERSION_MAJOR}::Network> Drumstick::RT ) set(TS_FILES translations/drumstick-widgets_cs.ts translations/drumstick-widgets_de.ts translations/drumstick-widgets_en.ts translations/drumstick-widgets_es.ts translations/drumstick-widgets_fr.ts translations/drumstick-widgets_gl.ts translations/drumstick-widgets_it.ts translations/drumstick-widgets_nl.ts translations/drumstick-widgets_ru.ts translations/drumstick-widgets_sr.ts translations/drumstick-widgets_sv.ts translations/drumstick-widgets_tr.ts translations/drumstick-widgets_zh_CN.ts ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_add_translation(QM_FILES ${TS_FILES}) else() qt_add_translation(QM_FILES ${TS_FILES}) endif() add_custom_target( update-widget-translations COMMAND Qt${QT_VERSION_MAJOR}::lupdate ${CMAKE_CURRENT_SOURCE_DIR} -I ${PROJECT_SOURCE_DIR}/library/include -I ${CMAKE_CURRENT_SOURCE_DIR} -I ${CMAKE_CURRENT_BINARY_DIR} -ts ${TS_FILES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Updating widget translations" ) add_custom_target( drumstick-widgets-translations ALL DEPENDS ${QM_FILES} ) if (UNIX) install( FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/drumstick ) endif () if(STATIC_DRUMSTICK) set_target_properties(drumstick-widgets PROPERTIES STATIC_LIB "libdrumstick-widgets" EXPORT_NAME Widgets ) else() # STATIC_DRUMSTICK set_target_properties(drumstick-widgets PROPERTIES VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH} SOVERSION ${PROJECT_VERSION_MAJOR} EXPORT_NAME Widgets # macOS: MACOSX_RPATH TRUE ) if (BUILD_FRAMEWORKS) set_target_properties(drumstick-widgets PROPERTIES FRAMEWORK ${BUILD_FRAMEWORKS} FRAMEWORK_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH} MACOSX_FRAMEWORK_IDENTIFIER "net.sourceforge.drumstick-widgets" MACOSX_FRAMEWORK_INFO_PLIST "${CMAKE_SOURCE_DIR}/cmake_admin/CustomFrameworkInfo.plist.in" ) endif() endif() # STATIC_DRUMSTICK install(TARGETS drumstick-widgets EXPORT drumstick-widgets-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} ) install(FILES ${drumstick-widgets_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/drumstick) install(EXPORT drumstick-widgets-targets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/drumstick NAMESPACE Drumstick:: ) export(EXPORT drumstick-widgets-targets NAMESPACE Drumstick:: FILE ${CMAKE_BINARY_DIR}/drumstick-widgets-targets.cmake ) include(CMakePackageConfigHelpers) write_basic_package_version_file(${CMAKE_BINARY_DIR}/drumstick-widgets-config-version.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY AnyNewerVersion ) configure_file( drumstick-widgets-config.cmake ${CMAKE_BINARY_DIR}/drumstick-widgets-config.cmake @ONLY ) install(FILES ${CMAKE_BINARY_DIR}/drumstick-widgets-config-version.cmake ${CMAKE_BINARY_DIR}/drumstick-widgets-config.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/drumstick ) drumstick-2.9.0/library/widgets/fluidsettingsdialog.h0000644000175000017500000000572414541630232022072 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef FLUIDSETTINGSDIALOG_H #define FLUIDSETTINGSDIALOG_H #include #include #include /** * @file fluidsettingsdialog.h * Declaration of the Fluidsynth configuration dialog */ namespace drumstick { namespace rt { class MIDIOutput; } namespace widgets { namespace Ui { class FluidSettingsDialog; } class FluidSettingsDialog : public QDialog { Q_OBJECT public: explicit FluidSettingsDialog(QWidget *parent = nullptr); ~FluidSettingsDialog(); void readSettings(); void writeSettings(); void changeSoundFont(const QString& fileName); void chkDriverProperties(QSettings* settings); public Q_SLOTS: void accept() override; void showEvent(QShowEvent *event) override; void restoreDefaults(); void showFileDialog(); void audioDriverChanged(const QString &text); void bufferTimeChanged(int value); void bufferSizeChanged(); public: static const QString QSTR_PREFERENCES; static const QString QSTR_INSTRUMENTSDEFINITION; static const QString QSTR_DATADIR; static const QString QSTR_DATADIR2; static const QString QSTR_AUDIODRIVER; static const QString QSTR_PERIODSIZE; static const QString QSTR_PERIODS; static const QString QSTR_SAMPLERATE; static const QString QSTR_CHORUS; static const QString QSTR_REVERB; static const QString QSTR_GAIN; static const QString QSTR_POLYPHONY; static const QString QSTR_BUFFERTIME; static const int DEFAULT_BUFFERTIME = 50; static const int DEFAULT_PERIODSIZE = 512; static const int DEFAULT_PERIODS = 8; static constexpr double DEFAULT_SAMPLERATE = 44100.0; static const int DEFAULT_CHORUS = 0; static const int DEFAULT_REVERB = 1; static constexpr double DEFAULT_GAIN = 1.0; static const int DEFAULT_POLYPHONY = 256; static const QString QSTR_PULSEAUDIO; private: QString defaultAudioDriver() const; bool checkRanges() const; void initBuffer(); QString driverVersion() const; bool driverVersionLessThan_2_2_8(); Ui::FluidSettingsDialog *ui; drumstick::rt::MIDIOutput *m_driver; QString m_defSoundFont; }; }} // namespace drumstick::widgets #endif // FLUIDSETTINGSDIALOG_H drumstick-2.9.0/library/widgets/macsynthsettingsdialog.h0000644000175000017500000000335514541630232022613 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef MacSynthSettingsDialog_H #define MacSynthSettingsDialog_H #include #include #include /** * @file macsynthsettingsdialog.h * Declaration of the Mac Synth configuration dialog */ namespace drumstick { namespace rt { class MIDIOutput; } namespace widgets { namespace Ui { class MacSynthSettingsDialog; } class MacSynthSettingsDialog : public QDialog { Q_OBJECT public: explicit MacSynthSettingsDialog(QWidget *parent = nullptr); ~MacSynthSettingsDialog(); void readSettings(); void writeSettings(); void changeSoundFont(const QString& fileName); public Q_SLOTS: void accept() override; void showEvent(QShowEvent *event) override; void restoreDefaults(); void showFileDialog(); private: Ui::MacSynthSettingsDialog *ui; drumstick::rt::MIDIOutput *m_driver; void checkDriver(QSettings* settings); }; }} // namespace drumstick::widgets #endif // MacSynthSettingsDialog_H drumstick-2.9.0/library/widgets/networksettingsdialog.h0000644000175000017500000000354314541630232022455 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef NETWORKSETTINGSDIALOG_H #define NETWORKSETTINGSDIALOG_H #include #include #include /** * @file networksettingsdialog.h * Declaration of the Network configuration dialog */ namespace drumstick { namespace widgets { namespace Ui { class NetworkSettingsDialog; } class NetworkSettingsDialog : public QDialog { Q_OBJECT public: explicit NetworkSettingsDialog(const bool forInput, QWidget *parent = nullptr); ~NetworkSettingsDialog(); void readSettings(); void writeSettings(); void chkInitialization(QSettings* settings); static const QString QSTR_ADDRESS_IPV4; static const QString QSTR_ADDRESS_IPV6; public Q_SLOTS: void accept() override; void showEvent(QShowEvent *event) override; void restoreDefaults(); void toggledIPv6(bool checked); private: Ui::NetworkSettingsDialog *ui; QObject *m_driver; bool m_input; }; }} // namespace drumstick::widgets #endif // NETWORKSETTINGSDIALOG_H drumstick-2.9.0/library/widgets/pianoscene.cpp0000644000175000017500000012616714541630232020512 0ustar pedropedro/* Virtual Piano Widget for Qt Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include #include #include #include #include #include #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) #include #else #include #endif #include #include "pianoscene.h" /** * @file pianoscene.cpp * Implementation of the Piano Scene */ /** * @class QGraphicsScene * The QGraphicsScene class provides a surface for managing a large number of 2D graphical items. * @see https://doc.qt.io/qt-5/qgraphicsscene.html */ namespace drumstick { namespace widgets { class PianoScene::PianoScenePrivate { public: PianoScenePrivate ( const int baseOctave, const int numKeys, const int startKey ): m_baseOctave( baseOctave ), m_numKeys( numKeys ), m_startKey( startKey ), m_minNote( 0 ), m_maxNote( 127 ), m_transpose( 0 ), m_showLabels( ShowNever ), m_alterations( ShowSharps ), m_octave( OctaveC4 ), m_orientation( HorizontalOrientation ), m_rawkbd( false ), m_keyboardEnabled( true ), m_mouseEnabled( true ), m_touchEnabled( true ), m_mousePressed( false ), m_velocity( 100 ), m_channel( 0 ), m_velocityTint( true ), m_handler( nullptr ), m_keybdMap( nullptr ), m_showColorScale( false ), m_hilightPalette(PianoPalette(PAL_SINGLE)), m_backgroundPalette(PianoPalette(PAL_KEYS)), m_foregroundPalette(PianoPalette(PAL_FONT)), m_useKeyPix( true ), m_usingNativeFilter( false ), m_octaveSubscript( true ) { } void saveData(QByteArray& buffer) { QDataStream ds(&buffer, QIODevice::WriteOnly); ds << m_minNote; ds << m_maxNote; ds << m_transpose; ds << m_showLabels; ds << m_alterations; ds << m_octave; ds << m_orientation; ds << m_rawkbd; ds << m_keyboardEnabled; ds << m_mouseEnabled; ds << m_touchEnabled; ds << m_mousePressed; ds << m_velocity; ds << m_channel; ds << m_velocityTint; ds << m_noteNames; ds << m_names_s; ds << m_names_f; ds << m_showColorScale; ds << m_hilightPalette; ds << m_backgroundPalette; ds << m_foregroundPalette; ds << m_useKeyPix; ds << m_keyPix[0]; ds << m_keyPix[1]; ds << m_usingNativeFilter; ds << m_octaveSubscript; } void loadData(QByteArray& buffer) { quint32 u; QDataStream ds(&buffer, QIODevice::ReadOnly); ds >> m_minNote; ds >> m_maxNote; ds >> m_transpose; ds >> u; m_showLabels = LabelVisibility(u); ds >> u; m_alterations = LabelAlteration(u); ds >> u; m_octave = LabelCentralOctave(u); ds >> u; m_orientation = LabelOrientation(u); ds >> m_rawkbd; ds >> m_keyboardEnabled; ds >> m_mouseEnabled; ds >> m_touchEnabled; ds >> m_mousePressed; ds >> m_velocity; ds >> m_channel; ds >> m_velocityTint; ds >> m_noteNames; ds >> m_names_s; ds >> m_names_f; ds >> m_showColorScale; ds >> m_hilightPalette; ds >> m_backgroundPalette; ds >> m_foregroundPalette; ds >> m_useKeyPix; ds >> m_keyPix[0]; ds >> m_keyPix[1]; ds >> m_usingNativeFilter; ds >> m_octaveSubscript; } QString noteName( PianoKey* key, bool richText ) { Q_ASSERT(key != nullptr); int note = key->getNote(); int num = (note + m_transpose + 12) % 12; int adj = ((note + m_transpose < 0) ? 2 : 1) - m_octave + 1; int oct = m_baseOctave + ((note + m_transpose) / 12) - adj; QString nameMask = QLatin1String(richText && m_octaveSubscript ? "%1%2" : "%1%2"); if (m_noteNames.isEmpty()) { QString name; if (!m_names_f.isEmpty() && !m_names_s.isEmpty()) { switch(m_alterations) { case ShowFlats: name = m_names_f.value(num); break; case ShowSharps: name = m_names_s.value(num); break; case ShowNothing: if (key->isBlack()) { return QString(); } name = m_names_s.value(num); break; default: break; } } if (m_octave==OctaveNothing) { return name; } else { return nameMask.arg(name).arg(oct); } } else { if (m_noteNames.length() == 128) { int n = m_baseOctave*12 + note + m_transpose; //qDebug() << Q_FUNC_INFO << n << note; if (n >= 0 && n < m_noteNames.length()) { return m_noteNames.value(n); } } else if (m_noteNames.length() >= 12) { if (m_octave==OctaveNothing) { return m_noteNames.value(num); } else { return nameMask.arg(m_noteNames.value(num)).arg(oct); } } return QString(); } } int m_baseOctave; int m_numKeys; int m_startKey; int m_minNote; int m_maxNote; int m_transpose; LabelVisibility m_showLabels; LabelAlteration m_alterations; LabelCentralOctave m_octave; LabelOrientation m_orientation; bool m_rawkbd; bool m_keyboardEnabled; bool m_mouseEnabled; bool m_touchEnabled; bool m_mousePressed; int m_velocity; int m_channel; bool m_velocityTint; PianoHandler *m_handler; KeyboardMap *m_keybdMap; QHash m_keys; QMap m_labels; QStringList m_noteNames; QStringList m_names_s; QStringList m_names_f; bool m_showColorScale; PianoPalette m_hilightPalette; PianoPalette m_backgroundPalette; PianoPalette m_foregroundPalette; bool m_useKeyPix; QPixmap m_keyPix[2]; bool m_usingNativeFilter; bool m_octaveSubscript; /* not serialized */ PianoKeybd* m_view; QMap m_touched; }; const int KEYWIDTH = 180; const int KEYHEIGHT = 720; static qreal sceneWidth(int keys) { return KEYWIDTH * qCeil( keys * 7.0 / 12.0 ); } /** * Constructor. * @param baseOctave octave base number * @param numKeys number of keys * @param startKey starting key * @param keyPressedColor highlight keys color * @param parent owner object */ PianoScene::PianoScene ( const int baseOctave, const int numKeys, const int startKey, const QColor& keyPressedColor, QObject * parent ) : QGraphicsScene( QRectF(0, 0, sceneWidth(numKeys), KEYHEIGHT), parent ), d(new PianoScenePrivate(baseOctave, numKeys, startKey)) { if (keyPressedColor.isValid()) { setKeyPressedColor(keyPressedColor); } QBrush hilightBrush(getKeyPressedColor()); d->m_view = dynamic_cast(parent); if (d->m_view != nullptr) { setFont(d->m_view->font()); } int upperLimit = d->m_numKeys + d->m_startKey; int adj = d->m_startKey % 12; if (adj >= 5) adj++; for(int i = d->m_startKey; i < upperLimit; ++i) { float x = 0; PianoKey* key = nullptr; KeyLabel* lbl = nullptr; int ocs = i / 12 * 7; int j = i % 12; if (j >= 5) j++; if ((j % 2) == 0) { x = (ocs + qFloor((j-adj) / 2.0)) * KEYWIDTH; key = new PianoKey( QRectF(x, 0, KEYWIDTH, KEYHEIGHT), false, i ); lbl = new KeyLabel(key); lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(0)); } else { x = (ocs + qFloor((j-adj) / 2.0)) * KEYWIDTH + KEYWIDTH * 0.6 + 1; key = new PianoKey( QRectF( x, 0, KEYWIDTH * 0.8 - 1, KEYHEIGHT * 0.6 ), true, i ); key->setZValue( 1 ); lbl = new KeyLabel(key); lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(1)); } addItem( key ); lbl->setFont(font()); key->setAcceptTouchEvents(true); key->setPressedBrush(hilightBrush); d->m_keys.insert(i, key); d->m_labels.insert(i, lbl); } hideOrShowKeys(); retranslate(); } /** * Destructor. */ PianoScene::~PianoScene() { } /** * Returns the calculated size of the scene. * @return the calculated size of the scene */ QSize PianoScene::sizeHint() const { return {static_cast(sceneWidth(d->m_numKeys)), KEYHEIGHT}; } /** * Assigns the computer keyboard note map. * @param map the computer keyboard note map. */ void PianoScene::setKeyboardMap(KeyboardMap *map) { d->m_keybdMap = map; } /** * Returns the computer keyboard note map. * @return the computer keyboard note map */ KeyboardMap *PianoScene::getKeyboardMap() const { return d->m_keybdMap; } /** * Gets the PianoHandler pointer to the note receiver. * * If this method returns null, then there is not a PianoHandler class assigned, * and then the signals noteOn() and noteOff() are emitted instead. * @return pointer to the PianoHandler class, if there is one assigned */ PianoHandler *PianoScene::getPianoHandler() const { return d->m_handler; } /** * Assigns a PianoHandler pointer for processing note events. * * When this member is used to assign a PianoHandler instance, then * the methods in that instance are called instead of emitting the * signals noteOn() and noteOff(). * @param handler pointer to a PianoHandler instance */ void PianoScene::setPianoHandler(PianoHandler *handler) { d->m_handler = handler; } /** * Returns the palette used for highlighting the played keys * @return The PianoPalette used to highlight the played keys */ PianoPalette PianoScene::getHighlightPalette() { return d->m_hilightPalette; } /** * Displays the note label over a highligted key * @param key the activated key */ void PianoScene::displayKeyOn(PianoKey* key) { key->setPressed(true); int n = key->getNote() + d->m_baseOctave*12 + d->m_transpose; QString s = QString("#%1 (%2)").arg(n).arg(d->noteName(key, false)); Q_EMIT signalName(s); KeyLabel* lbl = dynamic_cast(key->childItems().constFirst()); if (lbl != nullptr) { lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(key->isBlack() ? 3 : 2)); if (d->m_showLabels == ShowActivated) { lbl->setVisible(true); } } } /** * Displays highlighted the activated key with the supplied color and note velocity * @param key the activated key * @param color the highlight color * @param vel the MIDI note velocity */ void PianoScene::showKeyOn( PianoKey* key, QColor color, int vel ) { //qDebug() << Q_FUNC_INFO << key->getNote() << vel << color << d->m_velocityTint; if (d->m_velocityTint && (vel >= 0) && (vel < 128) && color.isValid() ) { QBrush hilightBrush(color.lighter(200 - vel)); key->setPressedBrush(hilightBrush); } else if (color.isValid()) { key->setPressedBrush(color); } displayKeyOn(key); } /** * Displays highlighted the activated key with the supplied note velocity * @param key the activated key * @param vel the MIDI note velocity */ void PianoScene::showKeyOn( PianoKey* key, int vel ) { setHighlightColorFromPolicy(key, vel); displayKeyOn(key); } /** * Displays as deactivated a key * @param key the deactivated key * @param vel the MIDI note velocity */ void PianoScene::showKeyOff( PianoKey* key, int vel) { Q_UNUSED(vel) key->setPressed(false); Q_EMIT signalName(QString()); KeyLabel* lbl = dynamic_cast(key->childItems().constFirst()); if (lbl != nullptr) { lbl->restoreColor(); if (d->m_showLabels == ShowActivated) { lbl->setVisible(false); } } } /** * Displays highlighted the corresponding key for a given MIDI note, with a color and MIDI velocity * @param note The MIDI note number * @param color The highlight color * @param vel The MIDI note velocity */ void PianoScene::showNoteOn( const int note, QColor color, int vel ) { //qDebug() << Q_FUNC_INFO << note << vel << color; int n = note - d->m_baseOctave*12 - d->m_transpose; if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n) && color.isValid()) showKeyOn(d->m_keys.value(n), color, vel); } /** * Displays highlighted the corresponding key for a given MIDI note, with MIDI velocity * @param note The MIDI note number * @param vel The MIDI note velocity */ void PianoScene::showNoteOn( const int note, int vel ) { //qDebug() << Q_FUNC_INFO << note << vel; int n = note - d->m_baseOctave*12 - d->m_transpose; if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n)) { showKeyOn(d->m_keys.value(n), vel); } } /** * Displays deactivated the corresponding key for a given MIDI note, with MIDI velocity * @param note The MIDI note number * @param vel The MIDI note velocity */ void PianoScene::showNoteOff( const int note, int vel ) { int n = note - d->m_baseOctave*12 - d->m_transpose; if ((note >= d->m_minNote) && (note <= d->m_maxNote) && d->m_keys.contains(n)) { showKeyOff(d->m_keys.value(n), vel); } } /** * Returns the base octave number. * @see setBaseOctave() * @return the base octave number */ int PianoScene::baseOctave() const { return d->m_baseOctave; } /** * Performs a Note On MIDI event for the given MIDI note number and velocity. * If a PianoHandler instance is assigned, its PianoHandler::noteOn() method is called, * otherwise the noteOn() signal is triggered. * @param note The MIDI note number * @param vel The MIDI velocity */ void PianoScene::triggerNoteOn( const int note, const int vel ) { int n = d->m_baseOctave*12 + note + d->m_transpose; if ((n >= d->m_minNote) && (n <= d->m_maxNote)) { if (d->m_handler != nullptr) { d->m_handler->noteOn(n, vel); } else { Q_EMIT noteOn(n, vel); } } } /** * Performs a Note Off MIDI event for the given MIDI note number and velocity. * If a PianoHandler instance is assigned, its PianoHandler::noteOff() method is called, * otherwise the noteOff() signal is triggered. * @param note The MIDI note number * @param vel The MIDI velocity */ void PianoScene::triggerNoteOff( const int note, const int vel ) { int n = d->m_baseOctave*12 + note + d->m_transpose; if ((n >= d->m_minNote) && (n <= d->m_maxNote)) { if (d->m_handler != nullptr) { d->m_handler->noteOff(n, vel); } else { Q_EMIT noteOff(n, vel); } } } /** * Assigns to the given key the highlight color from the active highlight palette * and the given MIDI velocity. * @param key The given piano key * @param vel The MIDI note velocity */ void PianoScene::setHighlightColorFromPolicy(PianoKey* key, int vel) { QColor c; //qDebug() << Q_FUNC_INFO << key->getNote() << vel << d->m_velocityTint; switch (d->m_hilightPalette.paletteId()) { case PAL_SINGLE: c = d->m_hilightPalette.getColor(0); break; case PAL_DOUBLE: c = d->m_hilightPalette.getColor(key->getType()); break; case PAL_CHANNELS: c = d->m_hilightPalette.getColor(d->m_channel); break; case PAL_HISCALE: c = d->m_hilightPalette.getColor(key->getDegree()); break; default: return; } if (c.isValid()) { if (d->m_velocityTint && (vel >= 0) && (vel < 128)) { QBrush h(c.lighter(200 - vel)); key->setPressedBrush(h); } else { key->setPressedBrush(c); } } } /** * Produces a MIDI Note On event and highlights the given key * @param key The given key */ void PianoScene::keyOn( PianoKey* key ) { triggerNoteOn(key->getNote(), d->m_velocity); showKeyOn(key, d->m_velocity); } /** * Produces a MIDI Note Off event and deactivates the given key * @param key The given key */ void PianoScene::keyOff( PianoKey* key ) { triggerNoteOff(key->getNote(), 0); showKeyOff(key, 0); } /** * Produces a MIDI Note On event and highlights the given key with the given pressure * @param key The given key * @param pressure The applied pressure */ void PianoScene::keyOn( PianoKey* key, qreal pressure ) { int vel = d->m_velocity * pressure; triggerNoteOn(key->getNote(), vel); showKeyOn(key, vel); } /** * Produces a MIDI Note Off event and deactivates the given key with the given pressure. * @param key The given key * @param pressure The applied pressure */ void PianoScene::keyOff( PianoKey* key, qreal pressure ) { int vel = d->m_velocity * pressure; triggerNoteOff(key->getNote(), vel); showKeyOff(key, vel); } /** * Produces a MIDI Note On event and highlights the corresponding key for the given MIDI note number. * @param note The given MIDI note number */ void PianoScene::keyOn(const int note) { if (d->m_keys.contains(note)) keyOn(d->m_keys.value(note)); else triggerNoteOn(note, d->m_velocity); } /** * Produces a MIDI Note Off event and deactivates the corresponding key for the given MIDI note number. * @param note The given MIDI note number */ void PianoScene::keyOff(const int note) { if (d->m_keys.contains(note)) keyOff(d->m_keys.value(note)); else triggerNoteOff(note, d->m_velocity); } /** * Returns whether the low level computer keyboard mode is enabled. * @return true if the low level computer keyboard mode is enabled */ bool PianoScene::getRawKeyboardMode() const { return d->m_rawkbd; } /** * Returns the piano key for the given scene point coordenates. * @param p The given scene point coordenates * @return */ PianoKey* PianoScene::getKeyForPos( const QPointF& p ) const { PianoKey* key = nullptr; QList ptitems = this->items(p, Qt::IntersectsItemShape, Qt::DescendingOrder); foreach(QGraphicsItem *itm, ptitems) { key = dynamic_cast(itm); if (key != nullptr) break; } return key; } /** * This event handler, for event mouseEvent, is reimplemented to receive mouse move events for the scene. * @param mouseEvent The mouse move event object pointer */ void PianoScene::mouseMoveEvent ( QGraphicsSceneMouseEvent * mouseEvent ) { if (d->m_mouseEnabled && (mouseEvent->source() == Qt::MouseEventNotSynthesized)) { if (d->m_mousePressed) { PianoKey* key = getKeyForPos(mouseEvent->scenePos()); PianoKey* lastkey = getKeyForPos(mouseEvent->lastScenePos()); if ((lastkey != nullptr) && (lastkey != key) && lastkey->isPressed()) { keyOff(lastkey); } if ((key != nullptr) && !key->isPressed()) { keyOn(key); } mouseEvent->accept(); return; } } } /** * This event handler, for event mouseEvent, is reimplemented to receive mouse press events for the scene. * @param mouseEvent The mouse press event object pointer */ void PianoScene::mousePressEvent ( QGraphicsSceneMouseEvent * mouseEvent ) { if (d->m_mouseEnabled && (mouseEvent->source() == Qt::MouseEventNotSynthesized)) { PianoKey* key = getKeyForPos(mouseEvent->scenePos()); if (key != nullptr && !key->isPressed()) { keyOn(key); d->m_mousePressed = true; mouseEvent->accept(); return; } } } /** * This event handler, for event mouseEvent, is reimplemented to receive mouse release events for the scene. * @param mouseEvent The mouse release event object pointer */ void PianoScene::mouseReleaseEvent ( QGraphicsSceneMouseEvent * mouseEvent ) { if (d->m_mouseEnabled && (mouseEvent->source() == Qt::MouseEventNotSynthesized)) { d->m_mousePressed = false; PianoKey* key = getKeyForPos(mouseEvent->scenePos()); if (key != nullptr && key->isPressed()) { keyOff(key); mouseEvent->accept(); return; } } } /** * Returns the note number for the given computer keyboard key code. * @param key The given computer keyboard key code * @return The note number */ int PianoScene::getNoteFromKey( const int key ) const { if (d->m_keybdMap != nullptr) { KeyboardMap::ConstIterator it = d->m_keybdMap->constFind(key); if ((it != d->m_keybdMap->constEnd()) && (it.key() == key)) { int note = it.value(); return note; } } return -1; } /** * Returns the piano key object corresponding to the given computer keyboard key. * @param key The given computer keyboard key * @return The Piano Key object pointer */ PianoKey* PianoScene::getPianoKey( const int key ) const { int note = getNoteFromKey(key); if (d->m_keys.contains(note)) return d->m_keys.value(note); return nullptr; } /** * This event handler, for event keyEvent, is reimplemented to receive keypress events. * @param keyEvent The computer keyboard pressed event */ void PianoScene::keyPressEvent ( QKeyEvent * keyEvent ) { if ( d->m_keyboardEnabled && !d->m_usingNativeFilter && !keyEvent->isAutoRepeat() ) // ignore auto-repeats { int keyid = d->m_rawkbd ? #if defined(Q_OS_MACOS) keyEvent->nativeVirtualKey() #else keyEvent->nativeScanCode() #endif : keyEvent->key(); int note = getNoteFromKey( keyid ); if (note > -1) { keyOn(note); keyEvent->accept(); return; } } keyEvent->ignore(); } /** * This event handler, for event keyEvent, is reimplemented to receive key release events. * @param keyEvent The computer keyboard released event */ void PianoScene::keyReleaseEvent ( QKeyEvent * keyEvent ) { if ( d->m_keyboardEnabled && !d->m_usingNativeFilter && !keyEvent->isAutoRepeat() ) // ignore auto-repeats { int keyid = d->m_rawkbd ? #if defined(Q_OS_MACOS) keyEvent->nativeVirtualKey() #else keyEvent->nativeScanCode() #endif : keyEvent->key(); int note = getNoteFromKey( keyid ); if (note > -1) { keyOff(note); keyEvent->accept(); return; } } keyEvent->ignore(); } /** * This method overrides QGraphicsScene::event(). * It does not add new logic, but calls the overrided function. * @param event The given event * @return true if the event was processed */ bool PianoScene::event(QEvent *event) { return QGraphicsScene::event(event); } /** * Deactivates all keys. */ void PianoScene::allKeysOff() { foreach(PianoKey* key, d->m_keys) { key->setPressed(false); } } /** * Assigns a single color for key highlight. This is an alternative to creating a * highlight palette with a single color and assigning it. * @see setHighlightPalette() * @param color Color for key highlight */ void PianoScene::setKeyPressedColor(const QColor& color) { if (color.isValid()) { d->m_hilightPalette = PianoPalette(PAL_SINGLE); d->m_hilightPalette.setColor(0, color); QBrush hilightBrush(color); for (PianoKey *key : std::as_const(d->m_keys)) { key->setPressedBrush(hilightBrush); } } } /** * Assigns the default highlight palette colors and assigns it to the scene. */ void PianoScene::resetKeyPressedColor() { d->m_hilightPalette.resetColors(); QBrush hilightBrush(getKeyPressedColor()); for (PianoKey *key : std::as_const(d->m_keys)) { key->setPressedBrush(hilightBrush); } } /** * Returns the minimum MIDI note number that will be displayed. * @return the minimum MIDI note number */ int PianoScene::getMinNote() const { return d->m_minNote; } /** * Hides or shows keys */ void PianoScene::hideOrShowKeys() { for (PianoKey *key : std::as_const(d->m_keys)) { int n = d->m_baseOctave*12 + key->getNote() + d->m_transpose; bool b = !(n > d->m_maxNote) && !(n < d->m_minNote); key->setVisible(b); } } /** * Assigns the minimum MIDI note number that will be displayed. * @param note the minimum MIDI note number */ void PianoScene::setMinNote(const int note) { if (d->m_minNote != note) { d->m_minNote = note; hideOrShowKeys(); } } /** * Returns the maximum MIDI note number that will be displayed. * @return the maximum MIDI note number */ int PianoScene::getMaxNote() const { return d->m_maxNote; } /** * Assigns the maximum MIDI note number that will be displayed. * @param note the maximum MIDI note number */ void PianoScene::setMaxNote(const int note) { if (d->m_maxNote != note) { d->m_maxNote = note; hideOrShowKeys(); } } /** * Returns the transpose amount in semitones. * @return the transpose amount in semitones */ int PianoScene::getTranspose() const { return d->m_transpose; } /** * Assigns the octave base number * @param base the octave base number */ void PianoScene::setBaseOctave(const int base) { if (d->m_baseOctave != base) { d->m_baseOctave = base; hideOrShowKeys(); refreshLabels(); } } /** * Returns the number of keys that will be displayed. * @return the number of keys */ int PianoScene::numKeys() const { return d->m_numKeys; } /** * Returns the first key number that will be displayed. * @return the first key number */ int PianoScene::startKey() const { return d->m_startKey; } /** * Returns whether the given note number is a octave startup note * @param note The given note number * @return true if the given note number is a octave startup note */ bool PianoScene::isOctaveStart(const int note) { return (note + d->m_transpose + 12) % 12 == 0; } /** * Returns the note name string that will be displayed over a given piano key. * @param key The given piano key * @return the note name string */ QString PianoScene::noteName( PianoKey* key ) { Q_ASSERT(key != nullptr); return d->noteName(key, true); } /** * Refresh the visibility and other attributes of the labels shown over the piano keys. */ void PianoScene::refreshLabels() { for (KeyLabel *lbl : std::as_const(d->m_labels)) { PianoKey* key = dynamic_cast(lbl->parentItem()); if (key != nullptr) { lbl->setVisible(false); lbl->setFont(font()); lbl->setDefaultTextColor(d->m_foregroundPalette.getColor(key->isBlack() ? 1 : 0)); lbl->setOrientation(d->m_orientation); lbl->setHtml(d->noteName(key, true)); lbl->adjust(); lbl->setVisible((d->m_showLabels == ShowAlways) || (d->m_showLabels == ShowMinimum && isOctaveStart(key->getNote()))); } } } /** * Refresh the background colors of all the piano keys */ void PianoScene::refreshKeys() { for (PianoKey *key : std::as_const(d->m_keys)) { if (d->m_showColorScale && (d->m_backgroundPalette.paletteId() == PAL_SCALE)) { int degree = key->getNote() % 12; key->setBrush(d->m_backgroundPalette.getColor(degree)); } else { key->setBrush(d->m_backgroundPalette.getColor(key->isBlack() ? 1 : 0)); } key->setPressed(false); } } /** * Assigns the label visibility policy to the piano keys * @see LabelVisibility * @param show the new label visibility policy */ void PianoScene::setShowLabels(const LabelVisibility show) { //qDebug() << Q_FUNC_INFO << show; if (d->m_showLabels != show) { d->m_showLabels = show; refreshLabels(); } } /** * Returns the alterations name policy. * @see LabelAlteration, setAlterations() * @return the alterations name policy */ LabelAlteration PianoScene::alterations() const { return d->m_alterations; } /** * Assigns the alterations name policy * @see LabelAlteration, alterations() * @param use the new alterations name policy */ void PianoScene::setAlterations(const LabelAlteration use) { if (d->m_alterations != use) { d->m_alterations = use; refreshLabels(); } } /** * Returns the central octave name policy. * @return the central octave name policy */ LabelCentralOctave PianoScene::getOctave() const { return d->m_octave; } /** * Assigns the label orientation policy. * @param orientation the label orientation policy */ void PianoScene::setOrientation(const LabelOrientation orientation) { if (d->m_orientation != orientation) { d->m_orientation = orientation; refreshLabels(); } } bool PianoScene::isKeyboardEnabled() const { return d->m_keyboardEnabled; } void PianoScene::setOctave(const LabelCentralOctave octave) { if (d->m_octave != octave) { d->m_octave = octave; refreshLabels(); } } LabelOrientation PianoScene::getOrientation() const { return d->m_orientation; } /** * Assigns the transpose amount in semitones. * @param transpose the transpose amount in semitones */ void PianoScene::setTranspose(const int transpose) { if (d->m_transpose != transpose && transpose > -12 && transpose < 12) { d->m_transpose = transpose; hideOrShowKeys(); refreshLabels(); } } /** * Returns the label visibility policy (display note names over the piano keys). * @see LabelVisibility, setShowLabels() * @return the label visibility policy */ LabelVisibility PianoScene::showLabels() const { return d->m_showLabels; } /** * Assigns the low level computer keyboard mode. * @param b the low level computer keyboard mode */ void PianoScene::setRawKeyboardMode(bool b) { if (d->m_rawkbd != b) { d->m_rawkbd = b; } } /** * Returns the custom note names list. * @return the custom note names list */ QStringList PianoScene::customNoteNames() const { return d->m_noteNames; } /** * Returns the standard note names list. * @return the standard note names list */ QStringList PianoScene::standardNoteNames() const { return d->m_names_s; } /** * Returns the MIDI note velocity parameter that is assigned to the MIDI OUT notes. * @return the MIDI note velocity */ int PianoScene::getVelocity() { return d->m_velocity; } /** * Assigns the MIDI note velocity parameter that is assigned to the MIDI OUT notes. * @param velocity the MIDI note velocity */ void PianoScene::setVelocity(const int velocity) { d->m_velocity = velocity; } /** * Returns the MIDI channel that is assigned to the output events, or used to filter * the input events (unless MIDI OMNI mode is enabled). * @return the MIDI channel */ int PianoScene::getChannel() const { return d->m_channel; } /** * Assigns the MIDI channel that is included into the output events, or used to filter * the input events (unless MIDI OMNI mode is enabled). * @param channel the MIDI channel */ void PianoScene::setChannel(const int channel) { d->m_channel = channel; } /** * Assigns the list of custom note names, and enables this mode. * @param names the list of custom note names */ void PianoScene::useCustomNoteNames(const QStringList& names) { //qDebug() << Q_FUNC_INFO << names; d->m_noteNames = names; refreshLabels(); } /** * Assigns the standard note names, clearing the list of custom note names. */ void PianoScene::useStandardNoteNames() { //qDebug() << Q_FUNC_INFO; d->m_noteNames.clear(); refreshLabels(); } /** * Enables or disables the computer keyboard note generation. * @param enable the computer keyboard note generation */ void PianoScene::setKeyboardEnabled(const bool enable) { if (enable != d->m_keyboardEnabled) { d->m_keyboardEnabled = enable; } } /** * Returns whether the computer keyboard note generation is enabled * @return true if the computer keyboard note generation is enabled */ bool PianoScene::isMouseEnabled() const { return d->m_mouseEnabled; } /** * Enables or disables the mouse note generation. * @param enable the mouse note generation */ void PianoScene::setMouseEnabled(const bool enable) { if (enable != d->m_mouseEnabled) { d->m_mouseEnabled = enable; } } /** * Returns whether the touch screen note generation is enabled. * @return true if the touch screen note generation is enabled */ bool PianoScene::isTouchEnabled() const { return d->m_touchEnabled; } /** * Enables or disables the touch screen note generation. * @param enable the touch screen note generation */ void PianoScene::setTouchEnabled(const bool enable) { if (enable != d->m_touchEnabled) { d->m_touchEnabled = enable; } } /** * Returns whether the velocity parameter of note events is used to influence the highlight key colors. * @return whether the velocity parameter of note events is used to influence the highlight key colors */ bool PianoScene::velocityTint() const { return d->m_velocityTint; } /** * Enables or disables the velocity parameter of note events to influence the highlight key colors. * @param enable the velocity parameter of note events to influence the highlight key colors */ void PianoScene::setVelocityTint(const bool enable) { //qDebug() << Q_FUNC_INFO << enable; d->m_velocityTint = enable; } /** * Retranslates the standard note names */ void PianoScene::retranslate() { d->m_names_s = QStringList{ tr("C"), tr("C♯"), tr("D"), tr("D♯"), tr("E"), tr("F"), tr("F♯"), tr("G"), tr("G♯"), tr("A"), tr("A♯"), tr("B")}; d->m_names_f = QStringList{ tr("C"), tr("D♭"), tr("D"), tr("E♭"), tr("E"), tr("F"), tr("G♭"), tr("G"), tr("A♭"), tr("A"), tr("B♭"), tr("B")}; refreshLabels(); } /** * Enables or disables the color scale key background mode. * @param show the color scale key background mode */ void PianoScene::setShowColorScale(const bool show) { if (d->m_showColorScale != show) { d->m_showColorScale = show; refreshKeys(); invalidate(); } } /** * Returns the single highlight palette color. * @return the single highlight palette color */ QColor PianoScene::getKeyPressedColor() const { return d->m_hilightPalette.getColor(0); } /** * Assigns the active highlight palette. * @param p the active highlight palette */ void PianoScene::setHighlightPalette( const PianoPalette& p ) { if (d->m_hilightPalette != p) { d->m_hilightPalette = p; refreshKeys(); invalidate(); } } /** * Returns the background palette. * @return the background palette */ PianoPalette PianoScene::getBackgroundPalette() { return d->m_backgroundPalette; } /** * Assigns the active background palette. * @param p the active background palette */ void PianoScene::setBackgroundPalette(const PianoPalette& p ) { if (d->m_backgroundPalette != p) { d->m_backgroundPalette = p; refreshKeys(); invalidate(); } } /** * Returns the active foreground palette. * @return the active foreground palette */ PianoPalette PianoScene::getForegroundPalette() { return d->m_foregroundPalette; } /** * Assigns the active foreground palette. * @param p the foreground palette */ void PianoScene::setForegroundPalette(const PianoPalette &p) { if (d->m_foregroundPalette != p) { d->m_foregroundPalette = p; refreshLabels(); invalidate(); } } /** * Returns whether the color scale mode is enabled. * @return true if the color scale mode is enabled */ bool PianoScene::showColorScale() const { return d->m_showColorScale; } void PianoScene::setKeyPicture(const bool natural, const QPixmap &pix) { d->m_keyPix[int(natural)] = pix; for (PianoKey *key : std::as_const(d->m_keys)) { if (key->isBlack() == !natural) { key->setPixmap(pix); } } } QPixmap PianoScene::getKeyPicture(const bool natural) { return d->m_keyPix[int(natural)]; } void PianoScene::setUseKeyPictures(const bool enable) { d->m_useKeyPix = enable; for (PianoKey *key : std::as_const(d->m_keys)) { key->setUsePixmap(enable); } } bool PianoScene::getUseKeyPictures() const { return d->m_useKeyPix; } void PianoScene::saveData(QByteArray &ba) { d->saveData(ba); } void PianoScene::loadData(QByteArray &ba) { d->loadData(ba); } /** * Process touch screen events, called by the view * @param touchEvent The given QTouchEvent* * @return true if the event was consumed, false otherwise. */ bool PianoScene::touchScreenEvent(QTouchEvent *touchEvent) { switch(touchEvent->type()) { case QEvent::TouchEnd: case QEvent::TouchCancel: { foreach(PianoKey *key, d->m_touched) { //qDebug() << "key:" << key->getNote() << key->isPressed(); if (key->isPressed()) { keyOff(key); } } d->m_touched.clear(); touchEvent->accept(); return true; } /* case (end and cancel touch events) */ case QEvent::TouchBegin: case QEvent::TouchUpdate: { QList touchPoints = #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) touchEvent->touchPoints(); #else touchEvent->points(); #endif bool hasPressure = #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) touchEvent->device()->capabilities().testFlag(QTouchDevice::Pressure); #else touchEvent->device()->capabilities().testFlag(QInputDevice::Capability::Pressure); #endif foreach(const QTouchEvent::TouchPoint& touchPoint, touchPoints) { //qDebug() << touchPoint.id() << touchPoint.state(); switch (touchPoint.state()) { #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) case Qt::TouchPointReleased: #else case QEventPoint::Released: #endif { PianoKey* key = d->m_touched.value(touchPoint.id()); if (key != nullptr) { //qDebug() << "key:" << key->getNote() << key->isPressed(); if (key->isPressed()) { if (hasPressure) { keyOff(key, touchPoint.pressure()); } else { keyOff(key); } } d->m_touched.remove(touchPoint.id()); } break; } /* case released state */ #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) case Qt::TouchPointPressed: #else case QEventPoint::Pressed: #endif { PianoKey* key = getKeyForPos( d->m_view->mapToScene( #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) touchPoint.pos().toPoint() #else touchPoint.position().toPoint() #endif )); if (key != nullptr) { //qDebug() << "key:" << key->getNote() << key->isPressed(); if (!key->isPressed()) { if (hasPressure) { keyOn(key, touchPoint.pressure()); } else { keyOn(key); } key->ensureVisible(); } d->m_touched[touchPoint.id()] = key; } break; } /* case pressed state */ #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) case Qt::TouchPointMoved: #else case QEventPoint::Updated: #endif { PianoKey* key = getKeyForPos( d->m_view->mapToScene( #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) touchPoint.pos().toPoint() #else touchPoint.position().toPoint() #endif )); PianoKey* lastkey = d->m_touched.value(touchPoint.id()); if ((lastkey != nullptr) && (lastkey != key)) { //qDebug() << "lastkey:" << lastkey->getNote() << lastkey->isPressed(); if (lastkey->isPressed()) { if (hasPressure) { keyOff(lastkey, touchPoint.pressure()); } else { keyOff(lastkey); } } d->m_touched.remove(touchPoint.id()); } if (key != nullptr) { //qDebug() << "key:" << key->getNote() << key->isPressed(); if (!key->isPressed()) { if (hasPressure) { keyOn(key, touchPoint.pressure()); } else { keyOn(key); } } d->m_touched[touchPoint.id()] = key; } break; } /* case updated state */ default: break; } /* switch touchpoint state */ } /* foreach touchPoint */ touchEvent->accept(); return true; } /* case (begin and update touch events) */ default: break; } /* switch touchEvent->type() */ return false; } /** * @brief Enables or disables the application level usage of a native event filter * @param newState of the application level usage of a native event filter */ void PianoScene::setUsingNativeFilter(const bool newState) { if (newState != d->m_usingNativeFilter) { d->m_usingNativeFilter = newState; } } /** * @brief Returns whether the application is filtering native events * @return true if the application is filtering native events */ bool PianoScene::isUsingNativeFilter() const { return d->m_usingNativeFilter; } /** * @brief Enables or disables the subscript octave designation * @param enable the subscript octave designation */ void PianoScene::setOctaveSubscript(const bool enable) { if (d->m_octaveSubscript != enable) { d->m_octaveSubscript = enable; refreshLabels(); } } /** * @brief Returns whether the subscript octave designation is enabled * @return true if the subscript octave designation is enabled */ bool PianoScene::octaveSubscript() const { return d->m_octaveSubscript; } } // namespace widgets } // namespace drumstick drumstick-2.9.0/library/widgets/pianoscene.h0000644000175000017500000001527214541630232020151 0ustar pedropedro/* Virtual Piano Widget for Qt Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #ifndef PIANOSCENE_H_ #define PIANOSCENE_H_ #include #include #include #include #include #include #include "pianokey.h" #include "keylabel.h" /** * @file pianoscene.h * PianoScene class declaration */ namespace drumstick { namespace widgets { /** * @addtogroup Widgets * @{ * * @class PianoScene * The PianoScene class is a QGraphicsScene composed by a number of graphics items: * the piano keys. */ class PianoScene : public QGraphicsScene { Q_OBJECT public: PianoScene(const int baseOctave, const int numKeys, const int startKey, const QColor &keyPressedColor = QColor(), QObject *parent = nullptr); ~PianoScene(); QSize sizeHint() const; void setKeyboardMap(KeyboardMap *map); KeyboardMap *getKeyboardMap() const; PianoHandler *getPianoHandler() const; void setPianoHandler(PianoHandler *handler); PianoPalette getHighlightPalette(); void setHighlightPalette(const PianoPalette &p); PianoPalette getBackgroundPalette(); void setBackgroundPalette(const PianoPalette &p); PianoPalette getForegroundPalette(); void setForegroundPalette(const PianoPalette &p); bool showColorScale() const; void setShowColorScale(const bool show); QColor getKeyPressedColor() const; void setKeyPressedColor(const QColor &color); void resetKeyPressedColor(); int getMinNote() const; void setMinNote(const int note); int getMaxNote() const; void setMaxNote(const int note); int getTranspose() const; void setTranspose(const int transpose); LabelVisibility showLabels() const; void setShowLabels(const LabelVisibility show); LabelAlteration alterations() const; void setAlterations(const LabelAlteration use); LabelCentralOctave getOctave() const; void setOctave(const LabelCentralOctave octave); LabelOrientation getOrientation() const; void setOrientation(const LabelOrientation orientation); bool isKeyboardEnabled() const; void setKeyboardEnabled(const bool enable); bool isMouseEnabled() const; void setMouseEnabled(const bool enable); bool isTouchEnabled() const; void setTouchEnabled(const bool enable); bool velocityTint() const; void setVelocityTint(const bool enable); bool isOctaveStart(const int note); void showNoteOn(const int note, QColor color, int vel = -1); void showNoteOn(const int note, int vel = -1); void showNoteOff(const int note, int vel = -1); int baseOctave() const; void setBaseOctave(const int base); int numKeys() const; int startKey() const; void allKeysOff(); void keyOn(const int note); void keyOff(const int note); bool getRawKeyboardMode() const; void setRawKeyboardMode(const bool b); void useCustomNoteNames(const QStringList &names); void useStandardNoteNames(); QStringList customNoteNames() const; QStringList standardNoteNames() const; int getVelocity(); void setVelocity(const int velocity); int getChannel() const; void setChannel(const int channel); void retranslate(); void refreshLabels(); void hideOrShowKeys(); void refreshKeys(); void setKeyPicture(const bool natural, const QPixmap &pix); QPixmap getKeyPicture(const bool natural); void setUseKeyPictures(const bool enable); bool getUseKeyPictures() const; void saveData(QByteArray &ba); void loadData(QByteArray &ba); bool touchScreenEvent(QTouchEvent *touchEvent); void setUsingNativeFilter(const bool newState); bool isUsingNativeFilter() const; void setOctaveSubscript(const bool enable); bool octaveSubscript() const; Q_SIGNALS: /** * This signal is emitted for each Note On MIDI event created using * the computer keyboard, mouse or touch screen. It is not emitted if * a PianoHandler has been assigned using setPianoHandler(). * @param n the MIDI note number * @param v the MIDI velocity */ void noteOn(int n, int v); /** * This signal is emitted for each Note Off MIDI event created using * the computer keyboard, mouse or touch screen. It is not emitted if * a PianoHandler has been assigned using setPianoHandler(). * @param n the MIDI note number * @param v the MIDI velocity */ void noteOff(int n, int v); /** * signalName is emitted for each note created, and contains a string * with the MIDI note number and the note name for each note on event. * @param name the MIDI note number and name */ void signalName(const QString &name); protected: void showKeyOn(PianoKey *key, QColor color, int vel); void showKeyOn(PianoKey *key, int vel); void showKeyOff(PianoKey *key, int vel); void displayKeyOn(PianoKey *key); void keyOn(PianoKey *key); void keyOff(PianoKey *key); void keyOn(PianoKey *key, qreal pressure); void keyOff(PianoKey *key, qreal pressure); PianoKey *getKeyForPos(const QPointF &p) const; PianoKey *getPianoKey(const int key) const; QString noteName(PianoKey *key); void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) override; void mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) override; void mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) override; void keyPressEvent(QKeyEvent *keyEvent) override; void keyReleaseEvent(QKeyEvent *keyEvent) override; bool event(QEvent *event) override; void triggerNoteOn(const int note, const int vel); void triggerNoteOff(const int note, const int vel); int getNoteFromKey(const int key) const; void setHighlightColorFromPolicy(PianoKey *key, const int vel); private: class PianoScenePrivate; QScopedPointer d; }; /** @} */ }} // namespace drumstick::widgets #endif /*PIANOSCENE_H_*/ drumstick-2.9.0/library/widgets/settingsfactory.cpp0000644000175000017500000000540614541630232021606 0ustar pedropedro/* Drumstick Widgets Copyright (C) 2018-2023 Pedro Lopez-Cabanillas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include /** * @file settingsfactory.cpp * Implementation of the Settings Factory class */ namespace drumstick { namespace widgets { /** * @brief SettingsFactory::s_fileName is a global string providing the file name * of the persistent settings using the INI file format */ QString SettingsFactory::s_fileName; /** * @brief SettingsFactory::setFileName sets the global file name for the * persistent settings and sets the INI format as well * @param name the new file name */ void SettingsFactory::setFileName(const QString name) { SettingsFactory::s_fileName = name; if (name.isEmpty()) { QSettings::setDefaultFormat(QSettings::NativeFormat); } else { QSettings::setDefaultFormat(QSettings::IniFormat); } } /** * @brief SettingsFactory::fileName returns the file name of the persistent settings * @return QString the global file name of the persistent settings */ QString SettingsFactory::fileName() { return s_fileName; } /** * @brief SettingsFactory::format returns the storage format of the persistent settings * @return QSettings::Format the storage format of the persistent settings */ QSettings::Format SettingsFactory::format() { return QSettings::defaultFormat(); } /** * @brief SettingsFactory::getQSettings creates and/or returns a QSettings object pointer * @return the internal QSettings object pointer */ QSettings* SettingsFactory::getQSettings() { if (m_settings.isNull()) { if (s_fileName.isEmpty() || QSettings::defaultFormat() == QSettings::NativeFormat) { m_settings.reset(new QSettings()); } else { m_settings.reset(new QSettings(s_fileName, QSettings::IniFormat)); } m_settings->setAtomicSyncRequired(true); } return m_settings.data(); } /** * @brief SettingsFactory::operator -> is equivalent to calling getQSettings() * @return the internal QSettings object pointer */ QSettings* SettingsFactory::operator->() { return getQSettings(); } } // namespace widgets } // namespace drumstick drumstick-2.9.0/library/widgets/sonivoxsettingsdialog.h0000644000175000017500000000433714541630232022473 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SonivoxSettingsDialog_H #define SonivoxSettingsDialog_H #include #include #include /** * @file sonivoxsettingsdialog.h * Definition of the Sonivox Synth configuration dialog */ namespace drumstick { namespace rt { class MIDIOutput; } namespace widgets { namespace Ui { class SonivoxSettingsDialog; } class SonivoxSettingsDialog : public QDialog { Q_OBJECT public: explicit SonivoxSettingsDialog(QWidget *parent = nullptr); ~SonivoxSettingsDialog(); void readSettings(); void writeSettings(); void chkDriverProperties(QSettings *settings); void changeSoundFont(const QString& fileName); static const QString QSTR_PREFERENCES; static const QString QSTR_BUFFERTIME; static const QString QSTR_REVERBTYPE; static const QString QSTR_REVERBAMT; static const QString QSTR_CHORUSTYPE; static const QString QSTR_CHORUSAMT; static const QString QSTR_SOUNDFONT; static const QString QSTR_DATADIR; static const QString QSTR_DATADIR2; public Q_SLOTS: void accept() override; void showEvent(QShowEvent *event) override; void restoreDefaults(); void showFileDialog(); private: Ui::SonivoxSettingsDialog *ui; drumstick::rt::MIDIOutput *m_driver; }; }} // namespace drumstick::widgets #endif // SonivoxSettingsDialog_H drumstick-2.9.0/library/drumstick-config.cmake.in0000644000175000017500000000101014541630232021046 0ustar pedropedroif (CMAKE_VERSION VERSION_LESS 3.14.0) message(FATAL_ERROR "Drumstick requires at least CMake version 3.14.0") endif() if ( NOT (QT_VERSION_MAJOR EQUAL @QT_VERSION_MAJOR@) ) message(FATAL_ERROR "Drumstick was built with Qt@QT_VERSION_MAJOR@, please do not mix Qt${QT_VERSION_MAJOR} when building your program.") endif() foreach(component ${${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS}) string(TOLOWER ${component} locomp) include(${CMAKE_CURRENT_LIST_DIR}/drumstick-${locomp}-config.cmake) endforeach() drumstick-2.9.0/library/CMakeLists.txt0000644000175000017500000000335314541630232016743 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] if(BUILD_ALSA AND ALSA_FOUND) add_subdirectory(alsa) endif() if (BUILD_FILE) add_subdirectory(file) endif() if (BUILD_RT) add_subdirectory(rt) add_subdirectory(rt-backends) if (BUILD_WIDGETS) add_subdirectory(widgets) if (NOT STATIC_DRUMSTICK) add_subdirectory(vpiano-plugin) endif() endif() endif() configure_file( drumstick-config.cmake.in ${CMAKE_BINARY_DIR}/drumstick-config.cmake @ONLY ) include(CMakePackageConfigHelpers) write_basic_package_version_file( ${CMAKE_BINARY_DIR}/drumstick-config-version.cmake VERSION ${PROJECT_VERSION} COMPATIBILITY AnyNewerVersion ) install(FILES include/drumstick.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install( FILES ${CMAKE_BINARY_DIR}/drumstick-config.cmake ${CMAKE_BINARY_DIR}/drumstick-config-version.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/drumstick ) drumstick-2.9.0/utils/0000755000175000017500000000000014541630232013673 5ustar pedropedrodrumstick-2.9.0/utils/Info.plist.app0000644000175000017500000000216314541630232016424 0ustar pedropedro NSPrincipalClass NSApplication NSHighResolutionCapable True CFBundleIconFile @ICON@ CFBundlePackageType APPL CFBundleGetInfoString Created by Qt/QMake CFBundleInfoDictionaryVersion 6.0 CFBundleDisplayName Sample Drumstick Application CFBundleSignature @TYPEINFO@ CFBundleExecutable @EXECUTABLE@ CFBundleIdentifier @BUNDLEIDENTIFIER@ CFBundleVersion @FULL_VERSION@ CFBundleShortVersionString @FULL_VERSION@ NSHumanReadableCopyright © 2006-2021, Pedro López-Cabanillas and others NOTE This file was generated by QMake/CMake. drumstick-2.9.0/utils/drumgrid/0000755000175000017500000000000014541630232015510 5ustar pedropedrodrumstick-2.9.0/utils/drumgrid/drumgrid.pro0000644000175000017500000000114114541630232020044 0ustar pedropedroTEMPLATE = app TARGET = drumstick-drumgrid QT += gui widgets #dbus CONFIG += c++11 CONFIG += qt thread exceptions CONFIG += lrelease static { CONFIG += link_prl } DESTDIR = ../../build/bin LRELEASE_DIR=. INCLUDEPATH += . ../../library/include LIBS = -L../../build/lib -ldrumstick-alsa -lasound include (../../global.pri) SOURCES += drumgridabout.cpp \ drumgridmain.cpp \ drumgrid.cpp \ drumgridmodel.cpp HEADERS += drumgridabout.h \ drumgrid.h \ drumgridmodel.h FORMS += drumgridabout.ui \ drumgrid.ui TRANSLATIONS += \ drumstick-drumgrid_en.ts \ drumstick-drumgrid_es.ts drumstick-2.9.0/utils/drumgrid/drumgrid.qrc0000644000175000017500000000020114541630232020025 0ustar pedropedro ../../icons/drumstick_32.png drumstick-2.9.0/utils/drumgrid/drumgrid.ui0000644000175000017500000000672114541630232017672 0ustar pedropedro DrumGrid 0 0 596 396 Drumstick Drum Grid :/drumstick.png:/drumstick.png QFrame::StyledPanel QFrame::Raised 2 64 16 start stop 10 250 100 Qt::Horizontal 0 0 596 25 File Help Connect... Quit About About Qt drumstick-2.9.0/utils/drumgrid/drumgridabout.cpp0000644000175000017500000000240314541630232021063 0ustar pedropedro/* Drumgrid test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "drumgridabout.h" About::About(QWidget *parent) : QDialog(parent) { ui.setupUi(this); QString aboutText = ui.AboutTextView->toHtml(); QString strver(QT_STRINGIFY(VERSION)); #ifdef REVISION strver.append("
"); strver.append(tr("Revision")); strver.append(" "); strver.append(QT_STRINGIFY(REVISION)); #endif aboutText.replace("%VERSION%", strver); aboutText.replace("%QT_VERSION%", qVersion()); ui.AboutTextView->setHtml(aboutText); } drumstick-2.9.0/utils/drumgrid/drumgridabout.h0000644000175000017500000000200014541630232020521 0ustar pedropedro/* Drumgrid test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ABOUT_H #define ABOUT_H #include #include "ui_drumgridabout.h" class About : public QDialog { Q_OBJECT public: explicit About(QWidget *parent = nullptr); private: Ui::AboutClass ui; }; #endif // ABOUT_H drumstick-2.9.0/utils/drumgrid/drumgridabout.ui0000644000175000017500000001347014541630232020724 0ustar pedropedro AboutClass 0 0 487 334 About false true <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Drum Grid %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> true QDialogButtonBox::Close buttonBox rejected() AboutClass close() 306 315 325 335 drumstick-2.9.0/utils/drumgrid/drumgridmain.cpp0000644000175000017500000001016614541630232020702 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include "drumgrid.h" const char* QSTR_APPNAME = "drumstick-drumgrid"; const char* QSTR_DOMAIN = "drumstick.sourceforge.net"; const char* PGM_DESCRIPTION = QT_TRANSLATE_NOOP("main", "Drumstick Drum Grid"); const char* errorstr = QT_TRANSLATE_NOOP("main", "Fatal error from the ALSA sequencer. " "This usually happens when the kernel doesn't have ALSA support, " "or the device node (/dev/snd/seq) doesn't exists, " "or the kernel module (snd_seq) is not loaded. " "Please check your ALSA/MIDI configuration."); int main(int argc, char *argv[]) { QApplication app(argc, argv); QCoreApplication::setOrganizationName(QSTR_DOMAIN); QCoreApplication::setOrganizationDomain(QSTR_DOMAIN); QCoreApplication::setApplicationName(QSTR_APPNAME); QCoreApplication::setApplicationVersion(QStringLiteral(QT_STRINGIFY(VERSION))); QGuiApplication::setDesktopFileName("net.sourceforge.drumstick-drumgrid"); QLocale locale; QTranslator qtTranslator; if ((locale.language() != QLocale::C) && (locale.language() != QLocale::English)) { if (qtTranslator.load( locale, "qt", "_", #if QT_VERSION < QT_VERSION_CHECK(6,0,0) QLibraryInfo::location(QLibraryInfo::TranslationsPath) #else QLibraryInfo::path(QLibraryInfo::TranslationsPath) #endif )) { QCoreApplication::installTranslator(&qtTranslator); } else { qWarning() << "Unable to load Qt translator:" << locale.name(); } } #if defined(Q_OS_WIN32) QString dataDir = QApplication::applicationDirPath() + "/"; #elif defined(Q_OS_MAC) QString dataDir = QApplication::applicationDirPath() + "/../Resources/"; #else QString dataDir = QApplication::applicationDirPath() + "/../share/drumstick/"; #endif QTranslator appTranslator; if ((locale.language() != QLocale::C) && (locale.language() != QLocale::English)) { if (appTranslator.load(locale, "drumstick-drumgrid", "_", dataDir)) { QCoreApplication::installTranslator(&appTranslator); } else { qWarning() << "Unable to load app translator:" << locale.name(); } } QCommandLineParser parser; parser.setApplicationDescription(PGM_DESCRIPTION); auto helpOption = parser.addHelpOption(); auto versionOption = parser.addVersionOption(); QCommandLineOption portOption({"p", "port"}, QCoreApplication::translate("main", "MIDI Out Port."), "client:port"); parser.addOption(portOption); parser.process(app); using drumstick::ALSA::SequencerError; try { DrumGrid w; if (parser.isSet(portOption)) { QString port = parser.value(portOption); w.subscribe(port); } w.show(); return app.exec(); } catch (const SequencerError& ex) { QMessageBox::critical(nullptr, QCoreApplication::translate("main", "Error"), QCoreApplication::translate("main", errorstr) + "\n" + QCoreApplication::translate("main", "Returned error was: ") + ex.qstrError() ); } catch (...) { qWarning() << QCoreApplication::translate("main", errorstr); } return EXIT_FAILURE; } drumstick-2.9.0/utils/drumgrid/drumstick-drumgrid.desktop0000644000175000017500000000040614541630232022723 0ustar pedropedro[Desktop Entry] Name=Drumstick Drum Grid Exec=drumstick-drumgrid Icon=drumstick Terminal=false Type=Application Categories=AudioVideo;Audio;Midi;Education;Music; Keywords=Music;Midi;Drum;Grid; Comment=Drumstick Drum Grid Comment[es]=Caja de Ritmos de Drumstick drumstick-2.9.0/utils/drumgrid/drumstick-drumgrid_en.ts0000644000175000017500000003116014541630232022363 0ustar pedropedro About Revision AboutClass About <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Drum Grid %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Drum Grid %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> DrumGrid Drumstick Drum Grid start stop File Help Connect... Quit About About Qt main Drumstick Drum Grid Fatal error from the ALSA sequencer. This usually happens when the kernel doesn't have ALSA support, or the device node (/dev/snd/seq) doesn't exists, or the kernel module (snd_seq) is not loaded. Please check your ALSA/MIDI configuration. MIDI Out Port. Error Returned error was: drumstick-2.9.0/utils/drumgrid/drumstick-drumgrid_es.ts0000644000175000017500000004255014541630232022375 0ustar pedropedro About Revision Revisión AboutClass About Acerca de <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Drum Grid %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Drum Grid %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Drum Grid %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> DrumGrid Drumstick Drum Grid Drumstick matriz de percusión start iniciar stop parar File Archivo Help Ayuda Connect... Conexión... Quit Terminar About Acerca de About Qt Acerca de Qt main Drumstick Drum Grid Drumstick matriz de percusión Fatal error from the ALSA sequencer. This usually happens when the kernel doesn't have ALSA support, or the device node (/dev/snd/seq) doesn't exists, or the kernel module (snd_seq) is not loaded. Please check your ALSA/MIDI configuration. Error fatal en el secuenciador ALSA. Esto ocurre habitualmente cuando el núcleo no tiene soporte ALSA, o bién el nodo de dispositivo (/dev/snd/seq) no existe, o el módulo del núcleo (snd_seq) no se ha cargado. Por favor compruebe su configuración de MIDI ALSA. MIDI Out Port. Puerto MIDI Out. Error Error Returned error was: El error devuelto fué: drumstick-2.9.0/utils/drumgrid/drumstick-drumgrid_it.ts0000644000175000017500000004264314541630232022405 0ustar pedropedro About Revision Revisione AboutClass About Informazioni <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Drum Grid %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Drum Grid %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Drum Grid %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Versione Qt: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Applicazione di esempio per la </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">libreria C++ Drumstick MIDI Sequencer</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">Questo programma è free software; lo si può ridistribuire e/o modificare in base ai termini della GNU General Public License come pubblicata dalla Free Software Foundation; sia nella versione 3 della Licenza, o (a scelta) in qualsiasi versione successiva.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">Questo programma è distribuito con la speranza che sia utile, ma SENZA QUALUNQUE GARANZIA; inclusa l'implicita garanzia di COMMERCIABILITÀ o di ADEGUATEZZA PER UNO SCOPO PARTICOLARE. Vedere la GNU General Public License per ulteriori dettagli.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">Dovreste avere ricevuto una copia della GNU General Public License insieme con questo programma. In caso contrario, vedere </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> DrumGrid Drumstick Drum Grid Drumstick Drum Grid start Avvio stop Arresto File File Help Aiuto Connect... Connetti... Quit Esci About Informazioni About Qt Informazioni su Qt main Drumstick Drum Grid Drumstick Drum Grid Fatal error from the ALSA sequencer. This usually happens when the kernel doesn't have ALSA support, or the device node (/dev/snd/seq) doesn't exists, or the kernel module (snd_seq) is not loaded. Please check your ALSA/MIDI configuration. Errore fatale dal sequencer ALSA. Questo solitamente accade quando il kernel non ha il supporto per ALSA, o quando il file "/dev/snd/seq" non esiste, oppure quando il modulo "snd_seq" non è stato caricato. Controllare la configurazione ALSA/MIDI del sistema. MIDI Out Port. Porta MIDI di uscita. Error Errore Returned error was: L'errore prodotto è: drumstick-2.9.0/utils/drumgrid/drumstick-drumgrid.metainfo.xml0000644000175000017500000000242314541630232023654 0ustar pedropedro net.sourceforge.drumstick-drumgrid drumstick-drumgrid Simple MIDI rhythm box based on ALSA Sequencer FSFAP GPL-3.0+

Simple MIDI software rhythm box based on percussion patterns and ALSA Sequencer.

This program is an example of the Drumstick libraries. For a fully featured rhythm utility see: kmetronome.sourceforge.io

net.sourceforge.drumstick-drumgrid.desktop https://drumstick.sourceforge.io/images/drumstick-drumgrid.png
drumstick-2.9.0/utils/drumgrid/CMakeLists.txt0000644000175000017500000000561114541630232020253 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(CMAKE_INCLUDE_CURRENT_DIR TRUE) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Gui Widgets LinguistTools REQUIRED) set(drumgrid_forms_SRCS drumgridabout.ui drumgrid.ui ) set(drumgrid_SRCS drumgridabout.cpp drumgridabout.h drumgrid.cpp drumgrid.h drumgrid.qrc drumgridmodel.cpp drumgridmodel.h drumgridmain.cpp ) set(drumgrid_qtobject_SRCS drumgridabout.h drumgridmodel.h drumgrid.h ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_ui(drumgrid_ui_SRCS ${drumgrid_forms_SRCS}) qt5_wrap_cpp(drumgrid_moc_SRCS ${drumgrid_qtobject_SRCS}) else() qt_wrap_ui(drumgrid_ui_SRCS ${drumgrid_forms_SRCS}) qt_wrap_cpp(drumgrid_moc_SRCS ${drumgrid_qtobject_SRCS}) endif() add_executable(drumstick-drumgrid ${drumgrid_ui_SRCS} ${drumgrid_moc_SRCS} ${drumgrid_SRCS} ) target_link_libraries(drumstick-drumgrid PRIVATE Drumstick::ALSA Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets ) set(TS_FILES drumstick-drumgrid_en.ts drumstick-drumgrid_es.ts drumstick-drumgrid_it.ts ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_add_translation(QM_FILES ${TS_FILES}) else() qt_add_translation(QM_FILES ${TS_FILES}) endif() add_custom_target( update-drumgrid-translations COMMAND Qt${QT_VERSION_MAJOR}::lupdate ${CMAKE_CURRENT_SOURCE_DIR} -ts ${TS_FILES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Updating drumgrid translations" ) add_custom_target( drumstick-drumgrid-translations ALL DEPENDS ${QM_FILES} ) if (UNIX AND NOT APPLE) install( FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/drumstick ) endif () install(TARGETS drumstick-drumgrid RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES drumstick-drumgrid.desktop DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications" RENAME net.sourceforge.drumstick-drumgrid.desktop) install(FILES drumstick-drumgrid.metainfo.xml DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo" RENAME net.sourceforge.drumstick-drumgrid.metainfo.xml) drumstick-2.9.0/utils/drumgrid/drumgrid.cpp0000644000175000017500000003127614541630232020042 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "drumgrid.h" #include "drumgridabout.h" #include "drumgridmodel.h" #include "ui_drumgrid.h" using namespace drumstick::ALSA; const QString DrumGrid::QSTR_WINDOW = QStringLiteral("Window"); const QString DrumGrid::QSTR_GEOMETRY = QStringLiteral("Geometry"); const QString DrumGrid::QSTR_STATE = QStringLiteral("State"); const QString DrumGrid::QSTR_MIDI = QStringLiteral("MIDI"); const QString DrumGrid::QSTR_CONNECTION = QStringLiteral("Connection"); const QString DrumGrid::QSTR_TEMPO = QStringLiteral("Tempo"); const QString DrumGrid::QSTR_PATTERN = QStringLiteral("Pattern"); const int DrumGrid::TEMPO_MIN = 25; const int DrumGrid::TEMPO_MAX = 250; const int DrumGrid::TEMPO_DEFAULT = 120; const int DrumGrid::NOTE_DURATION = 10; const int DrumGrid::METRONOME_CHANNEL = 9; const int DrumGrid::METRONOME_VELOCITY = 100; const int DrumGrid::METRONOME_PROGRAM = 0; const int DrumGrid::METRONOME_RESOLUTION = 120; const int DrumGrid::METRONOME_VOLUME = 100; const int DrumGrid::METRONOME_PAN = 64; const int DrumGrid::VOLUME_CC = 7; const int DrumGrid::PAN_CC = 10; DrumGrid::DrumGrid(QWidget *parent) : QMainWindow(parent), m_ui(new Ui::DrumGrid), m_clientId(-1), m_portId(-1), m_queueId(-1), m_tick(0), m_weak_velocity(METRONOME_VELOCITY / 2), m_strong_velocity(METRONOME_VELOCITY), m_program(METRONOME_PROGRAM), m_channel(METRONOME_CHANNEL), m_volume(METRONOME_VOLUME), m_pan(METRONOME_PAN), m_resolution(METRONOME_RESOLUTION), m_bpm(TEMPO_DEFAULT), m_noteDuration(NOTE_DURATION), m_autoconnect(false), m_playing(false), m_useNoteOff(true) { m_ui->setupUi(this); m_ui->startButton->setIcon(style()->standardIcon(QStyle::StandardPixmap(QStyle::SP_MediaPlay))); m_ui->startButton->setShortcut(Qt::Key_MediaPlay); m_ui->stopButton->setIcon(style()->standardIcon(QStyle::StandardPixmap(QStyle::SP_MediaStop))); m_ui->stopButton->setShortcut(Qt::Key_MediaStop); m_ui->tempoSlider->setMaximum(TEMPO_MAX); m_ui->tempoSlider->setMinimum(TEMPO_MIN); m_ui->tempoSlider->setValue(m_bpm); connect( m_ui->actionAbout, &QAction::triggered, this, &DrumGrid::slotAbout); connect( m_ui->actionAbout_Qt, &QAction::triggered, qApp, &QApplication::aboutQt); connect( m_ui->actionQuit, &QAction::triggered, this, &DrumGrid::close); connect( m_ui->actionConnect, &QAction::triggered, this, &DrumGrid::connectMidi); connect( m_ui->startButton, &QPushButton::clicked, this, &DrumGrid::play); connect( m_ui->stopButton, &QPushButton::clicked, this, &DrumGrid::stop); connect( m_ui->tempoSlider, &QSlider::valueChanged, this, &DrumGrid::tempoChange); connect( m_ui->gridColumns, QOverload::of(&QSpinBox::valueChanged), this, &DrumGrid::gridColumns); m_model = new DrumGridModel(this); m_model->fillSampleData(); m_ui->tableView->setModel(m_model); connect ( this, &DrumGrid::signalUpdate, this, &DrumGrid::updateDisplay ); addShortcut(QKeySequence(Qt::Key_F), "f"); addShortcut(QKeySequence(Qt::Key_P), "p"); addShortcut(QKeySequence(Qt::Key_1), "1"); addShortcut(QKeySequence(Qt::Key_2), "2"); addShortcut(QKeySequence(Qt::Key_3), "3"); addShortcut(QKeySequence(Qt::Key_4), "4"); addShortcut(QKeySequence(Qt::Key_5), "5"); addShortcut(QKeySequence(Qt::Key_6), "6"); addShortcut(QKeySequence(Qt::Key_7), "7"); addShortcut(QKeySequence(Qt::Key_8), "8"); addShortcut(QKeySequence(Qt::Key_9), "9"); addShortcut(QKeySequence(Qt::Key_0), QString()); addShortcut(QKeySequence::Delete, QString()); connect ( m_ui->tableView, &QTableView::doubleClicked, m_model, QOverload::of(&DrumGridModel::changeCell) ); m_Client = new MidiClient(this); m_Client->open(); m_Client->setClientName("DrumGrid"); connect( m_Client, &MidiClient::eventReceived, this, &DrumGrid::sequencerEvent, Qt::QueuedConnection ); m_Port = new MidiPort(this); m_Port->attach( m_Client ); m_Port->setPortName("DrumGrid Output Port"); m_Port->setCapability( SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_READ ); m_Port->setPortType( SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_MIDI_GENERIC ); m_Queue = m_Client->createQueue("DrumGrid"); m_queueId = m_Queue->getId(); m_portId = m_Port->getPortId(); m_clientId = m_Client->getClientId(); m_Client->setRealTimeInput(false); m_Client->startSequencerInput(); readSettings(); updateView(); } DrumGrid::~DrumGrid() { foreach(QShortcut* s, m_shortcuts) delete s; m_Port->detach(); m_Client->close(); delete m_ui; } void DrumGrid::updateView() { m_ui->tableView->resizeColumnsToContents(); m_ui->tableView->resizeRowsToContents(); } void DrumGrid::subscribe(const QString& portName) { try { if (!m_subscription.isEmpty()) { m_Port->unsubscribeTo(m_subscription); m_subscription.clear(); } m_Port->subscribeTo(portName); m_subscription = portName; } catch (const SequencerError& err) { qWarning() << "SequencerError exception. Error code: " << err.code() << " (" << err.qstrError() << ")"; qWarning() << "Location: " << err.location(); } } void DrumGrid::connectMidi() { bool ok; int current; QStringList items; QListIterator it(m_Client->getAvailableOutputs()); while(it.hasNext()) { PortInfo p = it.next(); items << QString("%1:%2").arg(p.getClientName()).arg(p.getPort()); } current = items.indexOf(m_subscription); QString item = QInputDialog::getItem(this, "MIDI port subscription", "Output port:", items, current, false, &ok); if (ok && !item.isEmpty()) { subscribe(item); } } void DrumGrid::sequencerEvent(SequencerEvent *ev) { switch (ev->getSequencerType()) { case SND_SEQ_EVENT_USR0: metronome_pattern(ev->getTick()); m_bar++; m_beat = 0; break; case SND_SEQ_EVENT_USR1: m_beat++; Q_EMIT signalUpdate(m_bar, m_beat-1); break; } delete ev; } void DrumGrid::play() { metronome_set_tempo(); metronome_start(); } void DrumGrid::stop() { metronome_stop(); } void DrumGrid::tempoChange(int newTempo) { QString tip = QString::number(newTempo); m_bpm = newTempo; metronome_set_tempo(); m_ui->tempoSlider->setToolTip(tip); QToolTip::showText(QCursor::pos(), tip, this); } void DrumGrid::gridColumns(int columns) { m_model->updatePatternColumns(columns); updateView(); } void DrumGrid::shortcutPressed(const QString& value) { QModelIndex index = m_ui->tableView->currentIndex(); m_model->changeCell(index, value); } void DrumGrid::addShortcut(const QKeySequence& key, const QString& value) { QShortcut* shortcut = new QShortcut(key, m_ui->tableView); connect(shortcut, &QShortcut::activated, this, [=]{ shortcutPressed(value); }); m_shortcuts.append(shortcut); } void DrumGrid::readSettings() { QSettings settings; settings.beginGroup(QSTR_WINDOW); restoreGeometry(settings.value(QSTR_GEOMETRY).toByteArray()); restoreState(settings.value(QSTR_STATE).toByteArray()); settings.endGroup(); settings.beginGroup(QSTR_MIDI); QString midiConn = settings.value(QSTR_CONNECTION).toString(); m_bpm = settings.value(QSTR_TEMPO, TEMPO_DEFAULT).toInt(); settings.endGroup(); if (midiConn.length() > 0) { subscribe(midiConn); } settings.beginGroup(QSTR_PATTERN); QStringList keys = settings.allKeys(); if (!keys.empty()) { keys.sort(); m_model->clearPattern(); foreach(const QString& key, keys) { QStringList row = settings.value(key).toStringList(); m_model->addPatternData(key.toInt(), row); } m_model->endOfPattern(); } settings.endGroup(); } void DrumGrid::writeSettings() { QSettings settings; settings.clear(); settings.beginGroup(QSTR_WINDOW); settings.setValue(QSTR_GEOMETRY, saveGeometry()); settings.setValue(QSTR_STATE, saveState()); settings.endGroup(); settings.beginGroup(QSTR_MIDI); settings.setValue(QSTR_CONNECTION, m_subscription); settings.setValue(QSTR_TEMPO, m_bpm); settings.endGroup(); settings.beginGroup(QSTR_PATTERN); for(int r = 0; r < m_model->rowCount(); ++r) { settings.setValue( m_model->patternKey(r), m_model->patternData(r) ); } settings.endGroup(); settings.sync(); } void DrumGrid::closeEvent( QCloseEvent *event ) { writeSettings(); event->accept(); } void DrumGrid::metronome_event_output(SequencerEvent* ev) { ev->setSource(m_portId); ev->setSubscribers(); ev->setDirect(); m_Client->outputDirect(ev); } void DrumGrid::sendControlChange(int cc, int value) { ControllerEvent ev(m_channel, cc, value); metronome_event_output(&ev); } void DrumGrid::sendInitialControls() { metronome_set_program(); metronome_set_controls(); metronome_set_tempo(); } void DrumGrid::metronome_set_program() { ProgramChangeEvent ev(m_channel, m_program); metronome_event_output(&ev); } void DrumGrid::metronome_schedule_event(SequencerEvent* ev, int tick) { ev->setSource(m_portId); if (ev->getSequencerType() >= SND_SEQ_EVENT_USR0) ev->setDestination(m_clientId, m_portId); else ev->setSubscribers(); ev->scheduleTick(m_queueId, tick, false); m_Client->outputDirect(ev); } void DrumGrid::metronome_note(int note, int vel, int tick) { if (m_useNoteOff) { NoteEvent ev(m_channel, note, vel, m_noteDuration); metronome_schedule_event(&ev, tick); } else { NoteOnEvent ev(m_channel, note, vel); metronome_schedule_event(&ev, tick); } } void DrumGrid::metronome_echo(int tick, int ev_type) { SystemEvent ev(ev_type); metronome_schedule_event(&ev, tick); } int DrumGrid::decodeVelocity(const QString drumVel) { const qreal f = 127.0 / 9.0; int num = 0; bool isNum = false; if (drumVel.isEmpty()) return 0; if (drumVel == "f") return m_strong_velocity; else if (drumVel == "p") return m_weak_velocity; num = drumVel.toInt(&isNum); if (isNum) return qRound(f * num); return 0; } void DrumGrid::metronome_pattern(int tick) { int i, j, t, duration, key, vel; t = tick; duration = m_resolution / 4; for(i=0; icolumnCount(); ++i) { for(j=0; jrowCount(); ++j) { QString n = m_model->patternHit(j, i); if (!n.isEmpty()) { key = m_model->patternKey(j).toInt(); vel = decodeVelocity(n); metronome_note(key, vel, t); } } metronome_echo(t, SND_SEQ_EVENT_USR1); t += duration; } metronome_echo(t, SND_SEQ_EVENT_USR0); } void DrumGrid::metronome_set_tempo() { QueueTempo t = m_Queue->getTempo(); t.setPPQ(m_resolution); t.setNominalBPM(m_bpm); m_Queue->setTempo(t); m_Client->drainOutput(); } void DrumGrid::metronome_set_controls() { sendControlChange(VOLUME_CC, m_volume); sendControlChange(PAN_CC, m_pan); } void DrumGrid::metronome_start() { m_Queue->start(); m_patternDuration = m_resolution * m_model->columnCount() / 4; metronome_pattern(0); m_bar = 1; m_beat = 0; m_playing = true; } void DrumGrid::metronome_stop() { m_Queue->stop(); m_playing = false; } void DrumGrid::updateDisplay(int /*bar*/, int beat) { m_ui->tableView->selectColumn(beat); } void DrumGrid::slotAbout() { About dlgAbout(this); dlgAbout.exec(); } drumstick-2.9.0/utils/drumgrid/drumgrid.h0000644000175000017500000000731214541630232017501 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DRUMGRID_H #define DRUMGRID_H #include #include #include namespace Ui { class DrumGrid; } namespace drumstick { namespace ALSA { class MidiClient; class MidiPort; class MidiQueue; class SequencerEvent; }} class DrumGridModel; class DrumGrid : public QMainWindow { Q_OBJECT public: explicit DrumGrid(QWidget *parent = nullptr); ~DrumGrid(); void subscribe(const QString& portName); void addShortcut(const QKeySequence& key, const QString& value); void readSettings(); void writeSettings(); void closeEvent( QCloseEvent *event ) override; void metronome_start(); void metronome_stop(); void metronome_continue(); void sendControlChange(int cc, int value); void sendInitialControls(); void metronome_set_controls(); void metronome_set_program(); void metronome_set_tempo(); void metronome_pattern(int tick); void metronome_echo(int tick, int ev_type); void metronome_note(int note, int vel, int tick); void metronome_schedule_event(drumstick::ALSA::SequencerEvent* ev, int tick); void metronome_event_output(drumstick::ALSA::SequencerEvent* ev); int decodeVelocity(const QString drumVel); static const QString QSTR_WINDOW; static const QString QSTR_GEOMETRY; static const QString QSTR_STATE; static const QString QSTR_MIDI; static const QString QSTR_CONNECTION; static const QString QSTR_TEMPO; static const QString QSTR_PATTERN; static const int TEMPO_MIN; static const int TEMPO_MAX; static const int TEMPO_DEFAULT; static const int NOTE_DURATION; static const int METRONOME_CHANNEL; static const int METRONOME_VELOCITY; static const int METRONOME_PROGRAM; static const int METRONOME_RESOLUTION; static const int METRONOME_VOLUME; static const int METRONOME_PAN; static const int VOLUME_CC; static const int PAN_CC; public Q_SLOTS: void slotAbout(); void updateView(); void sequencerEvent(drumstick::ALSA::SequencerEvent *ev); void connectMidi(); void play(); void stop(); void tempoChange(int newTempo); void gridColumns(int columns); void shortcutPressed(const QString& value); void updateDisplay(int bar, int beat); Q_SIGNALS: void signalUpdate(int bar, int beat); private: Ui::DrumGrid *m_ui; int m_clientId; int m_portId; int m_queueId; unsigned long m_tick; drumstick::ALSA::MidiClient* m_Client; drumstick::ALSA::MidiPort* m_Port; drumstick::ALSA::MidiQueue* m_Queue; DrumGridModel* m_model; QString m_subscription; QVector m_shortcuts; int m_bar; int m_beat; int m_weak_velocity; int m_strong_velocity; int m_program; int m_channel; int m_volume; int m_pan; int m_resolution; int m_bpm; int m_noteDuration; int m_patternDuration; bool m_autoconnect; bool m_playing; bool m_useNoteOff; }; #endif // DRUMGRID_H drumstick-2.9.0/utils/drumgrid/drumgridmodel.cpp0000644000175000017500000001174614541630232021063 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "drumgridmodel.h" #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) #include #else #include #endif const QString DrumGridModel::DEFVAL = QStringLiteral("f"); DrumGridModel::DrumGridModel(QObject *parent) : QAbstractTableModel(parent), m_columns(16), m_lastValue(DEFVAL) { m_modelData.clear(); m_keys.clear(); loadKeyNames(); } void DrumGridModel::loadKeyNames() { m_keyNames.clear(); m_keyNames[46] = QStringLiteral("Open HH"); m_keyNames[42] = QStringLiteral("Closed HH"); m_keyNames[39] = QStringLiteral("Hand Claps"); m_keyNames[38] = QStringLiteral("Snare Drum"); m_keyNames[36] = QStringLiteral("Bass Drum"); } void DrumGridModel::fillSampleData() { beginInsertRows(QModelIndex(), 0, 4); m_modelData.insert(0, QString(",p,,,,p,,,,p,,,,p,,").split(",")); m_modelData.insert(1, QString("f,,,p,f,,,p,f,,,p,f,,,p").split(",")); m_modelData.insert(2, QString(",,,,,,,,,,,,,4,4,").split(",")); m_modelData.insert(3, QString(",,,,f,,,,,,,,f,,,").split(",")); m_modelData.insert(4, QString("f,,,,p,,,,f,,,,p,,,").split(",")); m_keys.insert(0, 46); m_keys.insert(1, 42); m_keys.insert(2, 39); m_keys.insert(3, 38); m_keys.insert(4, 36); endInsertRows(); } int DrumGridModel::rowCount(const QModelIndex & /* parent */) const { return m_modelData.size(); } int DrumGridModel::columnCount(const QModelIndex & /* parent */) const { return m_columns; } QVariant DrumGridModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || role != Qt::DisplayRole) return QVariant(); return m_modelData[index.row()][index.column()].trimmed(); } QVariant DrumGridModel::headerData(int section , Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole) { if ( orientation == Qt::Horizontal ) { int m = section % 10; if (m == 9) m = 0; else m++; return QString::number(m); } else { int k = m_keys.value(section, 0); if (m_keyNames.contains(k)) return m_keyNames[k]; } } return QVariant(); } void DrumGridModel::changeCell(const QModelIndex &index) { QString after, before = m_modelData[index.row()][index.column()]; if (before.isEmpty()) after = m_lastValue; else after = QString(); changeCell(index, after); } void DrumGridModel::changeCell(const QModelIndex &index, const QString& newValue) { m_lastValue = m_modelData[index.row()][index.column()] = newValue; Q_EMIT dataChanged(index, index); } QStringList DrumGridModel::patternData(int row) { return m_modelData.value(row, QStringList()); } QString DrumGridModel::patternKey(int row) { return QString::number(m_keys.value(row, 0)); } void DrumGridModel::clearPattern() { beginRemoveRows(QModelIndex(), 0, rowCount()); m_modelData.clear(); m_keys.clear(); m_tempData.clear(); m_tempKeys.clear(); endRemoveRows(); } void DrumGridModel::addPatternData(int key, const QStringList& row) { m_tempKeys.prepend(key); m_tempData.prepend(row); } void DrumGridModel::endOfPattern() { beginInsertRows(QModelIndex(), 0, m_tempData.size()); m_keys = m_tempKeys; m_modelData = m_tempData; endInsertRows(); } void DrumGridModel::updatePatternColumns(int columns) { int diff = m_columns - columns; if (diff == 0) return; if (diff > 0) { beginRemoveColumns(QModelIndex(), columns, m_columns-1); for(int i=0; i columns ) m_modelData[i].removeLast(); } else { beginInsertColumns(QModelIndex(), columns, columns-diff-1); for(int i=0; i 0) endRemoveColumns(); else endInsertColumns(); } QString DrumGridModel::patternHit(int row, int col) { if ((col < m_columns) && (row < m_modelData.size())) return m_modelData[row][col].trimmed(); return QString(); } drumstick-2.9.0/utils/drumgrid/drumgridmodel.h0000644000175000017500000000404014541630232020515 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DRUMGRIDMODEL_H #define DRUMGRIDMODEL_H #include #include class DrumGridModel : public QAbstractTableModel { Q_OBJECT public: explicit DrumGridModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; void loadKeyNames(); void fillSampleData(); void clearPattern(); void addPatternData(int key, const QStringList& row); void endOfPattern(); QStringList patternData(int row); QString patternKey(int row); QString patternHit(int row, int col); void updatePatternColumns(int columns); static const QString DEFVAL; public Q_SLOTS: void changeCell(const QModelIndex &index); void changeCell(const QModelIndex &index, const QString& newValue); private: int m_columns; QString m_lastValue; QMap m_keyNames; QList m_modelData; QList m_tempData; QList m_keys; QList m_tempKeys; }; #endif /* DRUMGRIDMODEL_H */ drumstick-2.9.0/utils/dumpmid/0000755000175000017500000000000014541630232015332 5ustar pedropedrodrumstick-2.9.0/utils/dumpmid/dumpmid.cpp0000644000175000017500000004101014541630232017471 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "dumpmid.h" #include #include #include #include #include #include #include #include #include #include #include #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) #define right Qt::right #define left Qt::left #define hex Qt::hex #define dec Qt::dec #define endl Qt::endl #endif QTextStream cout(stdout, QIODevice::WriteOnly); QTextStream cerr(stderr, QIODevice::WriteOnly); using namespace drumstick::ALSA; QDumpMIDI::QDumpMIDI() : QObject(), m_Stopped(false) { m_Client = new MidiClient(this); m_Client->open(); m_Client->setClientName("DumpMIDI"); #ifndef USE_QEVENTS // using signals instead connect( m_Client, &MidiClient::eventReceived, this, &QDumpMIDI::sequencerEvent, Qt::DirectConnection ); /* note: there is no event loop to handle Qt::QueuedConnection */ #endif // enable here the callback event delivery // m_Client->setHandler(this); m_Port = new MidiPort(this); m_Port->attach( m_Client ); m_Port->setPortName("DumpMIDI port"); m_Port->setCapability( SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE ); m_Port->setPortType( SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_MIDI_GENERIC ); #ifdef WANT_TIMESTAMPS m_Queue = m_Client->createQueue("DumpMIDI"); m_Port->setTimestamping(true); //m_Port->setTimestampReal(true); m_Port->setTimestampQueue(m_Queue->getId()); #endif connect( m_Port, &MidiPort::subscribed, this, &QDumpMIDI::subscription); qDebug() << "Trying to subscribe from Announce"; m_Port->subscribeFromAnnounce(); } QDumpMIDI::~QDumpMIDI() { m_Port->detach(); delete m_Port; m_Client->close(); delete m_Client; } bool QDumpMIDI::stopped() { QReadLocker locker(&m_mutex); return m_Stopped; } void QDumpMIDI::stop() { QWriteLocker locker(&m_mutex); m_Stopped = true; } void QDumpMIDI::subscription(MidiPort*, Subscription* subs) { qDebug() << "Subscription made from" << subs->getSender()->client << ":" << subs->getSender()->port; } void QDumpMIDI::subscribe(const QString& portName) { try { qDebug() << "Trying to subscribe" << portName.toLocal8Bit().data(); m_Port->subscribeFrom(portName); } catch (const SequencerError& err) { cerr << "SequencerError exception. Error code: " << err.code() << " (" << err.qstrError() << ")" << endl; cerr << "Location: " << err.location() << endl; throw; } } void QDumpMIDI::run() { cout << "Press Ctrl+C to exit" << endl; #ifdef WANT_TIMESTAMPS cout << "___Ticks "; #endif cout << "Source_ Event_________________ Ch _Data__" << endl; try { #ifdef USE_QEVENTS m_Client->addListener(this); m_Client->setEventsEnabled(true); #endif m_Client->setRealTimeInput(false); m_Client->startSequencerInput(); #ifdef WANT_TIMESTAMPS m_Queue->start(); #endif m_Stopped = false; while (!stopped()) { #ifdef USE_QEVENTS QCoreApplication::sendPostedEvents(); #endif sleep(1); } #ifdef WANT_TIMESTAMPS m_Queue->stop(); #endif m_Client->stopSequencerInput(); } catch (const SequencerError& err) { cerr << "SequencerError exception. Error code: " << err.code() << " (" << err.qstrError() << ")" << endl; cerr << "Location: " << err.location() << endl; throw; } } void QDumpMIDI::handleSequencerEvent(SequencerEvent *ev) { //qDebug() << Q_FUNC_INFO; dumpEvent(ev); delete ev; } #ifdef USE_QEVENTS void QDumpMIDI::customEvent(QEvent *ev) { //qDebug() << Q_FUNC_INFO; if (ev->type() == SequencerEventType) { SequencerEvent* sev = static_cast(ev); if (sev != nullptr) { dumpEvent(sev); } } } #else void QDumpMIDI::sequencerEvent(SequencerEvent *ev) { //qDebug() << Q_FUNC_INFO; dumpEvent(ev); delete ev; } #endif void QDumpMIDI::dumpEvent(SequencerEvent* sev) { #ifdef WANT_TIMESTAMPS cout << qSetFieldWidth(8) << right << sev->getTick(); /* More timestamp options: cout << sev->getRealTimeSecs(); cout << sev->getRealTimeNanos(); */ /* Getting the time from the queue status object; QueueStatus sts = m_Queue->getStatus(); cout << qSetFieldWidth(8) << right << sts.getClockTime(); cout << sts.getTickTime(); */ cout << qSetFieldWidth(0) << " "; #endif cout << qSetFieldWidth(3) << right << sev->getSourceClient() << qSetFieldWidth(0) << ":"; cout << qSetFieldWidth(3) << left << sev->getSourcePort() << qSetFieldWidth(0) << " "; switch (sev->getSequencerType()) { case SND_SEQ_EVENT_NOTEON: { NoteOnEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(23) << left << "Note on"; cout << qSetFieldWidth(2) << right << e->getChannel() << " "; cout << qSetFieldWidth(3) << e->getKey() << " "; cout << qSetFieldWidth(3) << e->getVelocity(); } break; } case SND_SEQ_EVENT_NOTEOFF: { NoteOffEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(23) << left << "Note off"; cout << qSetFieldWidth(2) << right << e->getChannel() << " "; cout << qSetFieldWidth(3) << e->getKey() << " "; cout << qSetFieldWidth(3) << e->getVelocity(); } break; } case SND_SEQ_EVENT_KEYPRESS: { KeyPressEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(23) << left << "Polyphonic aftertouch"; cout << qSetFieldWidth(2) << right << e->getChannel() << " "; cout << qSetFieldWidth(3) << e->getKey() << " "; cout << qSetFieldWidth(3) << e->getVelocity(); } break; } case SND_SEQ_EVENT_CONTROL14: case SND_SEQ_EVENT_NONREGPARAM: case SND_SEQ_EVENT_REGPARAM: case SND_SEQ_EVENT_CONTROLLER: { ControllerEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(23) << left << "Control change"; cout << qSetFieldWidth(2) << right << e->getChannel() << " "; cout << qSetFieldWidth(3) << e->getParam() << " "; cout << qSetFieldWidth(3) << e->getValue(); } break; } case SND_SEQ_EVENT_PGMCHANGE: { ProgramChangeEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(23) << left << "Program change"; cout << qSetFieldWidth(2) << right << e->getChannel() << " "; cout << qSetFieldWidth(3) << e->getValue(); } break; } case SND_SEQ_EVENT_CHANPRESS: { ChanPressEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(23) << left << "Channel aftertouch"; cout << qSetFieldWidth(2) << right << e->getChannel() << " "; cout << qSetFieldWidth(3) << e->getValue(); } break; } case SND_SEQ_EVENT_PITCHBEND: { PitchBendEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(23) << left << "Pitch bend"; cout << qSetFieldWidth(2) << right << e->getChannel() << " "; cout << qSetFieldWidth(5) << e->getValue(); } break; } case SND_SEQ_EVENT_SONGPOS: { ValueEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "Song position pointer" << qSetFieldWidth(0); cout << e->getValue(); } break; } case SND_SEQ_EVENT_SONGSEL: { ValueEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "Song select" << qSetFieldWidth(0); cout << e->getValue(); } break; } case SND_SEQ_EVENT_QFRAME: { ValueEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "MTC quarter frame" << qSetFieldWidth(0); cout << e->getValue(); } break; } case SND_SEQ_EVENT_TIMESIGN: { ValueEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "SMF time signature" << qSetFieldWidth(0); cout << hex << e->getValue(); cout << dec; } break; } case SND_SEQ_EVENT_KEYSIGN: { ValueEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "SMF key signature" << qSetFieldWidth(0); cout << hex << e->getValue(); cout << dec; } break; } case SND_SEQ_EVENT_SETPOS_TICK: { QueueControlEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "Set tick queue pos." << qSetFieldWidth(0); cout << e->getQueue(); } break; } case SND_SEQ_EVENT_SETPOS_TIME: { QueueControlEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "Set rt queue pos." << qSetFieldWidth(0); cout << e->getQueue(); } break; } case SND_SEQ_EVENT_TEMPO: { TempoEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "Set queue tempo"; cout << qSetFieldWidth(3) << right << e->getQueue() << qSetFieldWidth(0) << " "; cout << e->getValue(); } break; } case SND_SEQ_EVENT_QUEUE_SKEW: { QueueControlEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "Queue timer skew" << qSetFieldWidth(0); cout << e->getQueue(); } break; } case SND_SEQ_EVENT_START: cout << left << "Start"; break; case SND_SEQ_EVENT_STOP: cout << left << "Stop"; break; case SND_SEQ_EVENT_CONTINUE: cout << left << "Continue"; break; case SND_SEQ_EVENT_CLOCK: cout << left << "Clock"; break; case SND_SEQ_EVENT_TICK: cout << left << "Tick"; break; case SND_SEQ_EVENT_TUNE_REQUEST: cout << left << "Tune request"; break; case SND_SEQ_EVENT_RESET: cout << left << "Reset"; break; case SND_SEQ_EVENT_SENSING: cout << left << "Active Sensing"; break; case SND_SEQ_EVENT_CLIENT_START: { ClientEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "Client start" << qSetFieldWidth(0) << e->getClient(); } break; } case SND_SEQ_EVENT_CLIENT_EXIT: { ClientEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "Client exit" << qSetFieldWidth(0) << e->getClient(); } break; } case SND_SEQ_EVENT_CLIENT_CHANGE: { ClientEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "Client changed" << qSetFieldWidth(0) << e->getClient(); } break; } case SND_SEQ_EVENT_PORT_START: { PortEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "Port start" << qSetFieldWidth(0); cout << e->getClient() << ":" << e->getPort(); } break; } case SND_SEQ_EVENT_PORT_EXIT: { PortEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "Port exit" << qSetFieldWidth(0); cout << e->getClient() << ":" << e->getPort(); } break; } case SND_SEQ_EVENT_PORT_CHANGE: { PortEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "Port changed" << qSetFieldWidth(0); cout << e->getClient() << ":" << e->getPort(); } break; } case SND_SEQ_EVENT_PORT_SUBSCRIBED: { SubscriptionEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "Port subscribed" << qSetFieldWidth(0); cout << e->getSenderClient() << ":" << e->getSenderPort() << " -> "; cout << e->getDestClient() << ":" << e->getDestPort(); } break; } case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: { SubscriptionEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "Port unsubscribed" << qSetFieldWidth(0); cout << e->getSenderClient() << ":" << e->getSenderPort() << " -> "; cout << e->getDestClient() << ":" << e->getDestPort(); } break; } case SND_SEQ_EVENT_SYSEX: { SysExEvent* e = static_cast(sev); if (e != nullptr) { cout << qSetFieldWidth(26) << left << "System exclusive" << qSetFieldWidth(0); unsigned int i; for (i = 0; i < e->getLength(); ++i) { cout << hex << (unsigned char) e->getData()[i] << " "; } cout << dec; } break; } default: cout << qSetFieldWidth(26) << "Unknown event type" << qSetFieldWidth(0); cout << sev->getSequencerType(); }; cout << qSetFieldWidth(0) << endl; } QDumpMIDI* test; void signalHandler(int sig) { if (sig == SIGINT) qDebug() << "Caught a SIGINT. Exiting"; else if (sig == SIGTERM) qDebug() << "Caught a SIGTERM. Exiting"; test->stop(); } int main(int argc, char **argv) { const QString ERRORSTR = QStringLiteral("Fatal error from the ALSA sequencer. " "This usually happens when the kernel doesn't have ALSA support, " "or the device node (/dev/snd/seq) doesn't exists, " "or the kernel module (snd_seq) is not loaded. " "Please check your ALSA/MIDI configuration."); const QString PGM_NAME = QStringLiteral("drumstick-dumpmid"); const QString PGM_DESCRIPTION = QStringLiteral("Drumstick command line utility for decoding MIDI events"); QCoreApplication app(argc, argv); QCoreApplication::setApplicationName(PGM_NAME); QCoreApplication::setApplicationVersion(QStringLiteral(QT_STRINGIFY(VERSION))); QCommandLineParser parser; parser.setApplicationDescription(PGM_DESCRIPTION); auto helpOption = parser.addHelpOption(); auto versionOption = parser.addVersionOption(); QCommandLineOption portOption({"p", "port"}, "Source MIDI Port.", "client:port"); parser.addOption(portOption); parser.process(app); if (parser.isSet(versionOption) || parser.isSet(helpOption)) { return 0; } try { test = new QDumpMIDI; signal(SIGINT, signalHandler); signal(SIGTERM, signalHandler); if (parser.isSet(portOption)) { QString portName = parser.value(portOption); test->subscribe(portName); } else { cerr << "Port argument is mandatory" << endl; parser.showHelp(); } test->run(); } catch (const SequencerError& ex) { cerr << ERRORSTR << " Returned error was: " << ex.qstrError() << endl; } catch (...) { cerr << ERRORSTR << endl; } delete test; return 0; } drumstick-2.9.0/utils/dumpmid/dumpmid.pro0000644000175000017500000000071414541630232017515 0ustar pedropedroTEMPLATE = app TARGET = drumstick-dumpmid #QT += dbus equals(QT_MAJOR_VERSION, 6) { QT += core5compat } CONFIG += c++11 cmdline CONFIG += qt console thread exceptions static { CONFIG += link_prl } DESTDIR = ../../build/bin INCLUDEPATH += . ../../library/include DEPENDPATH += . ../../library ../../library/include include (../../global.pri) # Input HEADERS += dumpmid.h SOURCES += dumpmid.cpp LIBS = -L$$OUT_PWD/../../build/lib \ -ldrumstick-alsa drumstick-2.9.0/utils/dumpmid/CMakeLists.txt0000644000175000017500000000256614541630232020103 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(dumpmid_SRCS dumpmid.cpp dumpmid.h ) set(dumpmid_qtobject_SRCS dumpmid.h ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(dumpmid_moc_SRCS ${dumpmid_qtobject_SRCS}) else() qt_wrap_cpp(dumpmid_moc_SRCS ${dumpmid_qtobject_SRCS}) endif() add_executable(drumstick-dumpmid ${dumpmid_moc_SRCS} ${dumpmid_SRCS} ) target_link_libraries(drumstick-dumpmid PRIVATE Drumstick::ALSA Qt${QT_VERSION_MAJOR}::Core ) install(TARGETS drumstick-dumpmid RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) drumstick-2.9.0/utils/dumpmid/dumpmid.h0000644000175000017500000000417114541630232017145 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef DUMPMIDI_H_ #define DUMPMIDI_H_ /* MidiClient can deliver SequencerEvents with only * signals or posting QEvents to the QApplication loop */ #undef USE_QEVENTS //#define USE_QEVENTS /* To get timestamped events from ALSA, you need a running queue */ //#undef WANT_TIMESTAMPS #define WANT_TIMESTAMPS #include #include #include #include #include #include #include class QDumpMIDI : public QObject, public drumstick::ALSA::SequencerEventHandler { Q_OBJECT public: QDumpMIDI(); virtual ~QDumpMIDI(); void dumpEvent(drumstick::ALSA::SequencerEvent* ev); void subscribe(const QString& portName); void stop(); bool stopped(); void run(); virtual void handleSequencerEvent(drumstick::ALSA::SequencerEvent* ev); public Q_SLOTS: void subscription( drumstick::ALSA::MidiPort* port, drumstick::ALSA::Subscription* subs ); #ifdef USE_QEVENTS protected: virtual void customEvent( QEvent *ev ); #else void sequencerEvent( drumstick::ALSA::SequencerEvent* ev ); #endif private: drumstick::ALSA::MidiClient* m_Client; drumstick::ALSA::MidiPort* m_Port; #ifdef WANT_TIMESTAMPS drumstick::ALSA::MidiQueue* m_Queue; #endif bool m_Stopped; QReadWriteLock m_mutex; }; #endif /*DUMPMIDI_H_*/ drumstick-2.9.0/utils/dumprmi/0000755000175000017500000000000014541630232015350 5ustar pedropedrodrumstick-2.9.0/utils/dumprmi/dumprmi.cpp0000644000175000017500000002426114541630232017536 0ustar pedropedro/* Standard RIFF MIDI File dump program Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "dumprmi.h" DISABLE_WARNING_PUSH DISABLE_WARNING_DEPRECATED_DECLARATIONS #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) #define right Qt::right #define left Qt::left #define endl Qt::endl #endif QTextStream cout(stdout, QIODevice::WriteOnly); QTextStream cerr(stderr, QIODevice::WriteOnly); using namespace drumstick::File; DumpRmid::DumpRmid(): m_currentTrack(0), m_engine(nullptr), m_rc(0) { QTextCodec *codec = QTextCodec::codecForName("UTF-8"); m_engine = new Rmidi(this); m_smf = new QSmf(this); m_riff = new Riff(this); m_smf->setTextCodec(codec); connect(m_engine, &Rmidi::signalRiffInfo, this, &DumpRmid::infoHandler); connect(m_engine, &Rmidi::signalRiffData, this, &DumpRmid::dataHandler); connect(m_smf, &QSmf::signalSMFHeader, this, &DumpRmid::headerEvent); connect(m_smf, &QSmf::signalSMFTrackStart, this, &DumpRmid::trackStartEvent); connect(m_smf, &QSmf::signalSMFTrackEnd, this, &DumpRmid::trackEndEvent); connect(m_smf, &QSmf::signalSMFNoteOn, this, &DumpRmid::noteOnEvent); connect(m_smf, &QSmf::signalSMFNoteOff, this, &DumpRmid::noteOffEvent); connect(m_smf, &QSmf::signalSMFKeyPress, this, &DumpRmid::keyPressEvent); connect(m_smf, &QSmf::signalSMFCtlChange, this, &DumpRmid::ctlChangeEvent); connect(m_smf, &QSmf::signalSMFPitchBend, this, &DumpRmid::pitchBendEvent); connect(m_smf, &QSmf::signalSMFProgram, this, &DumpRmid::programEvent); connect(m_smf, &QSmf::signalSMFChanPress, this, &DumpRmid::chanPressEvent); connect(m_smf, &QSmf::signalSMFSysex, this, &DumpRmid::sysexEvent); connect(m_smf, &QSmf::signalSMFSeqSpecific, this, &DumpRmid::seqSpecificEvent); connect(m_smf, &QSmf::signalSMFMetaUnregistered, this, &DumpRmid::metaMiscEvent); connect(m_smf, &QSmf::signalSMFSequenceNum, this, &DumpRmid::seqNum); connect(m_smf, &QSmf::signalSMFforcedChannel, this, &DumpRmid::forcedChannel); connect(m_smf, &QSmf::signalSMFforcedPort, this, &DumpRmid::forcedPort); connect(m_smf, &QSmf::signalSMFText, this, &DumpRmid::textEvent); connect(m_smf, &QSmf::signalSMFendOfTrack, this, &DumpRmid::endOfTrackEvent); connect(m_smf, &QSmf::signalSMFTimeSig, this, &DumpRmid::timeSigEvent); connect(m_smf, &QSmf::signalSMFSmpte, this, &DumpRmid::smpteEvent); connect(m_smf, &QSmf::signalSMFKeySig, this, &DumpRmid::keySigEvent); connect(m_smf, &QSmf::signalSMFTempo, this, &DumpRmid::tempoEvent); connect(m_smf, &QSmf::signalSMFError, this, &DumpRmid::errorHandler); connect(m_riff, &Riff::signalDLS, this, &DumpRmid::processDLS); connect(m_riff, &Riff::signalInstrument, this, &DumpRmid::processInstrument); connect(m_riff, &Riff::signalPercussion, this, &DumpRmid::processPercussion); cout.setRealNumberNotation(QTextStream::FixedNotation); cout.setRealNumberPrecision(4); #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) cout.setCodec(codec); #else cout.setEncoding(QStringConverter::Utf8); #endif } void DumpRmid::dump(const QString& chan, const QString& event, const QString& data) { cout << right << qSetFieldWidth(7) << m_smf->getCurrentTime(); cout << right << qSetFieldWidth(10) << m_smf->getRealTime() / 1600.0; cout << qSetFieldWidth(3) << chan; cout << qSetFieldWidth(0) << left << " "; cout << qSetFieldWidth(15) << event; cout << qSetFieldWidth(0) << " " << data << endl; } void DumpRmid::dumpStr(const QString& event, const QString& data) { cout << right << qSetFieldWidth(7) << m_smf->getCurrentTime(); cout << right << qSetFieldWidth(10) << m_smf->getRealTime() / 1600.0; cout << qSetFieldWidth(3) << "--"; cout << qSetFieldWidth(0) << left << " "; cout << qSetFieldWidth(15) << event; cout << qSetFieldWidth(0) << " " << data << endl; } void DumpRmid::headerEvent(int format, int ntrks, int division) { dumpStr("SMF Header", QString("Format=%1, Tracks=%2, Division=%3") .arg(format).arg(ntrks).arg(division)); } void DumpRmid::trackStartEvent() { m_currentTrack++; dumpStr("Track", QString("Start: %1").arg(m_currentTrack)); } void DumpRmid::trackEndEvent() { dumpStr("Track", QString("End: %1").arg(m_currentTrack)); } void DumpRmid::endOfTrackEvent() { dumpStr("Meta Event", "End Of Track"); } void DumpRmid::noteOnEvent(int chan, int pitch, int vol) { dump(QString("%1").arg(chan, 2), "Note On", QString("%1, %2").arg(pitch).arg(vol)); } void DumpRmid::noteOffEvent(int chan, int pitch, int vol) { dump(QString("%1").arg(chan, 2), "Note Off", QString("%1, %2").arg(pitch).arg(vol)); } void DumpRmid::keyPressEvent(int chan, int pitch, int press) { dump(QString("%1").arg(chan, 2), "Key Pressure", QString("%1, %2").arg(pitch).arg(press)); } void DumpRmid::ctlChangeEvent(int chan, int ctl, int value) { dump(QString("%1").arg(chan, 2), "Control Change", QString("%1, %2").arg(ctl).arg(value)); } void DumpRmid::pitchBendEvent(int chan, int value) { dump(QString("%1").arg(chan, 2), "Pitch Bend", QString::number(value)); } void DumpRmid::programEvent(int chan, int patch) { dump(QString("%1").arg(chan, 2), "Program Change", QString::number(patch)); } void DumpRmid::chanPressEvent(int chan, int press) { dump(QString("%1").arg(chan, 2), "Chan.Pressure", QString::number(press)); } void DumpRmid::sysexEvent(const QByteArray& data) { int j; QString s; for (j = 0; j < data.count(); ++j) s.append(QString("%1 ").arg(data[j] & 0xff, 2, 16)); dumpStr("SysEx", s); } void DumpRmid::seqSpecificEvent(const QByteArray& data) { int j; QString s; for (j = 0; j < data.count(); ++j) s.append(QString("%1 ").arg(data[j] & 0xff, 2, 16)); dumpStr("Seq. specific", s); } void DumpRmid::metaMiscEvent(int typ, const QByteArray& data) { int j; QString s = QString("type=%1 ").arg(typ); for (j = 0; j < data.count(); ++j) s.append(QString("%1 ").arg(data[j] & 0xff, 2, 16)); dumpStr("Meta (unreg.)", s); } void DumpRmid::seqNum(int seq) { dump("--", "Sequence num.", QString::number(seq)); } void DumpRmid::forcedChannel(int channel) { dump("--", "Forced channel", QString::number(channel)); } void DumpRmid::forcedPort(int port) { dump("--", "Forced port", QString::number(port)); } void DumpRmid::textEvent(int typ, const QString& data) { dumpStr(QString("Text (%1)").arg(typ), data); } void DumpRmid::smpteEvent(int b0, int b1, int b2, int b3, int b4) { dump("--", "SMPTE", QString("%1, %2, %3, %4, %5").arg(b0).arg(b1).arg(b2).arg(b3).arg(b4)); } void DumpRmid::timeSigEvent(int b0, int b1, int b2, int b3) { dump("--", "Time Signature", QString("%1, %2, %3, %4").arg(b0).arg(b1).arg(b2).arg(b3)); } void DumpRmid::keySigEvent(int b0, int b1) { dump("--", "Key Signature", QString("%1, %2").arg(b0).arg(b1)); } void DumpRmid::tempoEvent(int tempo) { dump("--", "Tempo", QString::number(tempo)); } void DumpRmid::errorHandler(const QString& errorStr) { m_rc++; cerr << "*** Warning! " << errorStr << " at file offset " << m_smf->getFilePos() << endl; } void DumpRmid::infoHandler(const QString &infoType, const QByteArray &data) { QString info = QString::fromLatin1(data); if (m_extract) { cout << infoType << ": " << info << endl; } else { dump("--", "Info", QString("%1: %2").arg(infoType, info)); } } void DumpRmid::extractFileData(const QString &fileSuffix, const QByteArray &data) { QFileInfo finfo(m_fileName); QString outfile = QDir::current().absoluteFilePath(finfo.baseName() + fileSuffix); QFile file(outfile); file.open(QFile::WriteOnly); file.write(data); file.close(); } void DumpRmid::dataHandler(const QString &dataType, const QByteArray &data) { if (m_extract) { if (dataType == "RMID") { extractFileData(QLatin1String(".mid"), data); } else if (dataType == "DLS") { extractFileData(QLatin1String(".dls"), data); } } else { if (dataType == "RMID") { QDataStream ds(data); m_smf->readFromStream(&ds); } else if (dataType == "DLS") { cout << " __bank ____PC name_____________________" << endl; QDataStream ds(data); m_riff->readFromStream(&ds); } } } void DumpRmid::processDLS(QString name, QString version, QString copyright) { cout << "DLS: " << name; if (!version.isEmpty()) { cout << " version: " << version; } if (!copyright.isNull()) { cout << " copyright: " << copyright; } cout << endl; } void DumpRmid::processInstrument(int bank, int pc, QString name) { cout << right << qSetFieldWidth(7) << bank; cout << right << qSetFieldWidth(7) << pc; cout << left << qSetFieldWidth(0) << " " << name << endl; } void DumpRmid::processPercussion(int bank, int pc, QString name) { cout << right << qSetFieldWidth(7) << bank; cout << right << qSetFieldWidth(7) << pc; cout << left << qSetFieldWidth(0) << " " << name << endl; } void DumpRmid::run(QString fileName) { m_fileName = fileName; m_currentTrack = 0; if (!m_extract) { cout << "__ticks __seconds ch event__________ data____" << endl; } m_engine->readFromFile(fileName); } int DumpRmid::numErrors() { return m_rc; } void DumpRmid::setExtract(bool enable) { m_extract = enable; } DISABLE_WARNING_POP drumstick-2.9.0/utils/dumprmi/main.cpp0000644000175000017500000000533314541630232017004 0ustar pedropedro/* Standard RIFF MIDI File dump program Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include "dumprmi.h" #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) #define right Qt::right #define left Qt::left #define endl Qt::endl #endif using namespace drumstick::File; int main(int argc, char **argv) { const QString PGM_NAME = QStringLiteral("drumstick-dumprmi"); const QString PGM_DESCRIPTION = QStringLiteral("Drumstick command line utility for decoding RMI (RIFF MIDI) files"); QTextStream cerr(stderr, QIODevice::WriteOnly); DumpRmid spy; QCoreApplication app(argc, argv); QCoreApplication::setApplicationName(PGM_NAME); QCoreApplication::setApplicationVersion(QStringLiteral(QT_STRINGIFY(VERSION))); QCommandLineParser parser; parser.setApplicationDescription(PGM_DESCRIPTION); auto helpOption = parser.addHelpOption(); auto versionOption = parser.addVersionOption(); QCommandLineOption extractOption({{"e", "extract"}, "Extracts the embedded standard MIDI file."}); parser.addOption(extractOption); parser.addPositionalArgument("file", "Input RMI file name.", "files..."); parser.process(app); if (parser.isSet(versionOption) || parser.isSet(helpOption)) { return 0; } spy.setExtract(parser.isSet(extractOption)); QStringList fileNames, positionalArgs = parser.positionalArguments(); if (positionalArgs.isEmpty()) { cerr << "Input file name(s) missing" << endl; parser.showHelp(); } foreach(const QString& a, positionalArgs) { QFileInfo f(a); if (f.exists()) fileNames += f.canonicalFilePath(); else cerr << "File not found: " << a << endl; } int totalErrors = 0; foreach(const QString& file, fileNames) { spy.run(file); totalErrors += spy.numErrors(); } return totalErrors; } drumstick-2.9.0/utils/dumprmi/dumprmi.pro0000644000175000017500000000110514541630232017544 0ustar pedropedroTEMPLATE = app TARGET = drumstick-dumprmi CONFIG += c++11 cmdline qt equals(QT_MAJOR_VERSION, 6) { QT += core5compat } static { CONFIG += link_prl DEFINES += DRUMSTICK_STATIC } DESTDIR = ../../build/bin INCLUDEPATH += . ../../library/include include (../../global.pri) # Input HEADERS += dumprmi.h riff.h SOURCES += dumprmi.cpp main.cpp riff.cpp macx:!static { QMAKE_LFLAGS += -F$$OUT_PWD/../../build/lib -L$$OUT_PWD/../../build/lib LIBS += -framework drumstick-file } else { LIBS = -L$$OUT_PWD/../../build/lib \ -l$$drumstickLib(drumstick-file) } drumstick-2.9.0/utils/dumprmi/CMakeLists.txt0000644000175000017500000000302514541630232020110 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(dumprmi_SRCS dumprmi.cpp dumprmi.h main.cpp riff.cpp ) set(dumprmi_qtobject_SRCS dumprmi.h riff.h ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(dumprmi_moc_SRCS ${dumprmi_qtobject_SRCS}) else() qt_wrap_cpp(dumprmi_moc_SRCS ${dumprmi_qtobject_SRCS}) endif() add_executable(drumstick-dumprmi ${dumprmi_moc_SRCS} ${dumprmi_SRCS} ) target_link_libraries(drumstick-dumprmi PRIVATE Drumstick::File Qt${QT_VERSION_MAJOR}::Core ) if(QT_VERSION VERSION_GREATER_EQUAL 6.0.0) target_link_libraries(drumstick-dumprmi PRIVATE Qt6::Core5Compat) endif() install(TARGETS drumstick-dumprmi RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) drumstick-2.9.0/utils/dumprmi/dumprmi.h0000644000175000017500000000534114541630232017201 0ustar pedropedro/* Standard RIFF MIDI File dump program Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include "riff.h" class DumpRmid : public QObject { Q_OBJECT public: DumpRmid(); void run(QString fileName); void dump(const QString& chan, const QString& event, const QString& data); void dumpStr(const QString& event, const QString& data); int numErrors(); void setExtract(bool enable); void extractFileData(const QString& fileSuffix, const QByteArray& data); public Q_SLOTS: void headerEvent(int format, int ntrks, int division); void trackStartEvent(); void trackEndEvent(); void endOfTrackEvent(); void noteOnEvent(int chan, int pitch, int vol); void noteOffEvent(int chan, int pitch, int vol); void keyPressEvent(int chan, int pitch, int press); void ctlChangeEvent(int chan, int ctl, int value); void pitchBendEvent(int chan, int value); void programEvent(int chan, int patch); void chanPressEvent(int chan, int press); void sysexEvent(const QByteArray& data); void seqSpecificEvent(const QByteArray& data); void metaMiscEvent(int typ, const QByteArray& data); void seqNum(int seq); void forcedChannel(int channel); void forcedPort(int port); void textEvent(int typ, const QString& data); void smpteEvent(int b0, int b1, int b2, int b3, int b4); void timeSigEvent(int b0, int b1, int b2, int b3); void keySigEvent(int b0, int b1); void tempoEvent(int tempo); void errorHandler(const QString& errorStr); void infoHandler(const QString& infoType, const QByteArray& data); void dataHandler(const QString& dataType, const QByteArray& data); void processDLS(QString name, QString version, QString copyright); void processInstrument(int bank, int pc, QString name); void processPercussion(int bank, int pc, QString name); private: int m_currentTrack; drumstick::File::Rmidi *m_engine; drumstick::File::QSmf *m_smf; Riff *m_riff; int m_rc; bool m_extract; QString m_fileName; }; drumstick-2.9.0/utils/dumprmi/riff.cpp0000644000175000017500000002023614541630232017005 0ustar pedropedro/* Standard RIFF MIDI File dump program Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include "riff.h" Riff::Riff(QObject* parent) : QObject(parent) {} Riff::~Riff() {} #if 0 static QString toString(quint32 ckid) { QByteArray data((char*) &ckid, 4); return QString(data); } #endif void Riff::readFromFile(QString fileName) { QFile file(m_fileName = fileName); file.open(QIODevice::ReadOnly); QDataStream ds(&file); readFromStream(&ds); file.close(); } void Riff::readFromStream(QDataStream* ds) { if (ds != nullptr) { m_IOStream = ds; m_IOStream->setByteOrder(QDataStream::LittleEndian); read(); } } quint16 Riff::read16bit() { quint16 wrk; *m_IOStream >> wrk; return wrk; } quint32 Riff::read32bit() { quint32 wrk; *m_IOStream >> wrk; return wrk; } QString Riff::readstr(int size) { QScopedPointer> buffer(new char[size + 1]); m_IOStream->readRawData(buffer.data(), size); buffer.data()[size] = 0; return QString(buffer.data()).trimmed(); } void Riff::skip(int size) { if (size & 1) size++; m_IOStream->skipRawData(size); } quint32 Riff::readExpectedChunk(quint32 cktype) { quint32 chunkType, len = 0; chunkType = read32bit(); if (chunkType == cktype) { len = read32bit(); // qDebug() << "Expected chunkType:" << toString(chunkType) // << "(" << hex << chunkType << ")" // << "length:" << dec << len; } return len; } quint32 Riff::readChunk(quint32& chunkType) { quint32 len = 0; chunkType = read32bit(); len = read32bit(); // qDebug() << "chunkType:" << toString(chunkType) // << "(" << hex << chunkType << ")" // << "length:" << dec << len; return len; } quint32 Riff::readChunkID() { quint32 chunkID = read32bit(); // qDebug() << "chunkID:" << toString(chunkID) // << "(" << hex << chunkID << ")"; return chunkID; } QString Riff::readSFVersion() { int sfVersion = read16bit(); int sfMinor = read16bit(); return QString("%1.%2").arg(sfVersion).arg(sfMinor); } QString Riff::readDLSVersion() { quint16 v[4]; for(int i=0; i<4; ++i) v[i] = read16bit(); return QString("%1.%2.%3.%4").arg(v[0]).arg(v[1]).arg(v[2]).arg(v[3]); } void Riff::processINFO(int size) { quint32 chunkID = 0; quint32 length = 0; while (size > 0) { if (m_IOStream->atEnd()) return; length = readChunk(chunkID); if (length & 1) length++; size -= 8; switch (chunkID) { case CKID_IFIL: m_version = readSFVersion(); break; case CKID_INAM: m_name = readstr(length); break; case CKID_ICOP: m_copyright = readstr(length); break; default: skip(length); } size -= length; } } void Riff::processPHDR(int size) { int i, pc, bank; char name[21]; int npresets = size / 38 - 1; for (i = 0; i < npresets; i++) { if (m_IOStream->atEnd()) return; m_IOStream->readRawData(name, 20); name[20] = 0; pc = read16bit(); bank = read16bit(); skip(14); if (bank < 128) Q_EMIT signalInstrument(bank, pc, QString(name)); else Q_EMIT signalPercussion(bank, pc, QString(name)); //qDebug() << "Instrument: " << bank << pc << name; } skip(38); } void Riff::processPDTA(int size) { quint32 chunkID = 0; quint32 length; while (size > 0) { if (m_IOStream->atEnd()) return; length = readChunk(chunkID); size -= 8; if (m_IOStream->atEnd()) return; if (chunkID == CKID_PHDR) processPHDR(length); else skip(length); size -= length; } } void Riff::processSFList(int size) { quint32 chunkID = 0; if (m_IOStream->atEnd()) return; chunkID = readChunkID(); size -= 4; switch (chunkID) { case CKID_INFO: processINFO(size); break; case CKID_PDTA: processPDTA(size); break; default: skip(size); } } void Riff::processINSH(quint32& bank, quint32& pc, bool& perc) { read32bit(); bank = read32bit(); pc = read32bit(); perc = (bank & 0x80000000) != 0; bank &= 0x3FFF; } void Riff::processINS(int size) { bool perc = false; quint32 bank = 0, pc = 0; quint32 chunkID = 0; int length; while (size > 0) { if (m_IOStream->atEnd()) return; length = readChunk(chunkID); size -= 8; switch (chunkID) { case CKID_INSH: processINSH(bank, pc, perc); break; case CKID_LIST: processDLSList(length); break; default: skip(length); } size -= length; } //qDebug() << "Instrument:" << bank << pc << m_name; if (perc) Q_EMIT signalPercussion(bank, pc, m_name); else Q_EMIT signalInstrument(bank, pc, m_name); m_name.clear(); m_copyright.clear(); } void Riff::processLINS(int size) { quint32 chunkID = 0; int length; while (size > 0) { if (m_IOStream->atEnd()) return; length = readChunk(chunkID); size -= 8; if (chunkID == CKID_LIST) processDLSList(length); else skip(length); size -= length; } } void Riff::processDLSList(int size) { quint32 chunkID = 0; if (m_IOStream->atEnd()) return; chunkID = readChunkID(); size -= 4; switch (chunkID) { case CKID_INFO: processINFO(size); break; case CKID_LINS: processLINS(size); break; case CKID_INS: processINS(size); break; default: skip(size); } } void Riff::processDLS(int size) { quint32 chunkID = 0; int length; while (size > 0) { if (m_IOStream->atEnd()) return; length = readChunk(chunkID); size -= 8; switch (chunkID) { case CKID_VERS: m_version = readDLSVersion(); //qDebug() << "Version: " << m_version; break; case CKID_LIST: processDLSList(length); break; default: skip(length); } size -= length; } //qDebug() << "DLS:" << m_name << m_version << m_copyright; Q_EMIT signalDLS(m_name, m_version, m_copyright); } void Riff::processSF(int size) { quint32 chunkID = 0; int length; while (size > 0) { if (m_IOStream->atEnd()) return; length = readChunk(chunkID); size -= 8; if (m_IOStream->atEnd()) return; if (chunkID == CKID_LIST) processSFList(length); else skip(length); size -= length; } //qDebug() << "SoundFont:" << m_name << m_version << m_copyright; Q_EMIT signalSoundFont(m_name, m_version, m_copyright); } void Riff::read() { quint32 chunkID; quint32 length = readExpectedChunk(CKID_RIFF); if (length > 0) { chunkID = readChunkID(); length -= 4; switch(chunkID) { case CKID_DLS: //qDebug() << "DLS format"; processDLS(length); break; case CKID_SFBK: //qDebug() << "SoundFont format"; processSF(length); break; default: qWarning() << "Unsupported format"; } } } drumstick-2.9.0/utils/dumprmi/riff.h0000644000175000017500000000505414541630232016453 0ustar pedropedro/* Standard RIFF MIDI File dump program Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef RIFF_H #define RIFF_H #include #include #define CKID_RIFF 0x46464952 #define CKID_LIST 0x5453494c #define CKID_SFBK 0x6b626673 #define CKID_INFO 0x4f464e49 #define CKID_PDTA 0x61746470 #define CKID_IFIL 0x6c696669 #define CKID_INAM 0x4d414e49 #define CKID_PHDR 0x72646870 #define CKID_DLS 0x20534C44 #define CKID_COLH 0x686c6f63 #define CKID_VERS 0x73726576 #define CKID_LINS 0x736e696c #define CKID_ICOP 0x504f4349 #define CKID_INS 0x20736e69 #define CKID_INSH 0x68736e69 class Riff : public QObject { Q_OBJECT public: Riff(QObject* parent = nullptr); virtual ~Riff(); void readFromFile(QString fileName); void readFromStream(QDataStream* ds); Q_SIGNALS: void signalSoundFont(QString name, QString version, QString copyright); void signalDLS(QString name, QString version, QString copyright); void signalInstrument(int bank, int pc, QString name); void signalPercussion(int bank, int pc, QString name); private: /* SoundFont */ void processSF(int size); void processSFList(int size); void processPDTA(int size); void processPHDR(int size); QString readSFVersion(); /* DLS */ void processDLS(int size); void processDLSList(int size); void processLINS(int size); void processINS(int size); void processINSH(quint32& bank, quint32& pc, bool& perc); QString readDLSVersion(); /* common */ void read(); void processINFO(int size); quint32 readExpectedChunk(quint32 cktype); quint32 readChunk(quint32& cktype); quint32 readChunkID(); quint16 read16bit(); quint32 read32bit(); QString readstr(int size); void skip(int size); QDataStream *m_IOStream; QString m_fileName; QString m_name; QString m_copyright; QString m_version; }; #endif // RIFF_H drumstick-2.9.0/utils/dumpsmf/0000755000175000017500000000000014541630232015346 5ustar pedropedrodrumstick-2.9.0/utils/dumpsmf/dumpsmf.cpp0000644000175000017500000002247514541630232017537 0ustar pedropedro/* Standard MIDI File dump program Copyright (C) 2006-2023, Pedro Lopez-Cabanillas Based on midifile.c by Tim Thompson, M.Czeiszperger and Greg Lee This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "dumpsmf.h" #include #include #include #include #include #include #include #include #include DISABLE_WARNING_PUSH DISABLE_WARNING_DEPRECATED_DECLARATIONS #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) #define right Qt::right #define left Qt::left #define endl Qt::endl #endif QTextStream cout(stdout, QIODevice::WriteOnly); QTextStream cerr(stderr, QIODevice::WriteOnly); using drumstick::File::QSmf; QSpySMF::QSpySMF(): m_currentTrack(0), m_engine(nullptr), m_rc(0) { QTextCodec *codec = QTextCodec::codecForName("UTF-8"); m_engine = new QSmf(this); m_engine->setTextCodec(codec); connect(m_engine, &QSmf::signalSMFHeader, this, &QSpySMF::headerEvent); connect(m_engine, &QSmf::signalSMFTrackStart, this, &QSpySMF::trackStartEvent); connect(m_engine, &QSmf::signalSMFTrackEnd, this, &QSpySMF::trackEndEvent); connect(m_engine, &QSmf::signalSMFNoteOn, this, &QSpySMF::noteOnEvent); connect(m_engine, &QSmf::signalSMFNoteOff, this, &QSpySMF::noteOffEvent); connect(m_engine, &QSmf::signalSMFKeyPress, this, &QSpySMF::keyPressEvent); connect(m_engine, &QSmf::signalSMFCtlChange, this, &QSpySMF::ctlChangeEvent); connect(m_engine, &QSmf::signalSMFPitchBend, this, &QSpySMF::pitchBendEvent); connect(m_engine, &QSmf::signalSMFProgram, this, &QSpySMF::programEvent); connect(m_engine, &QSmf::signalSMFChanPress, this, &QSpySMF::chanPressEvent); connect(m_engine, &QSmf::signalSMFSysex, this, &QSpySMF::sysexEvent); connect(m_engine, &QSmf::signalSMFSeqSpecific, this, &QSpySMF::seqSpecificEvent); connect(m_engine, &QSmf::signalSMFMetaUnregistered, this, &QSpySMF::metaMiscEvent); connect(m_engine, &QSmf::signalSMFSequenceNum, this, &QSpySMF::seqNum); connect(m_engine, &QSmf::signalSMFforcedChannel, this, &QSpySMF::forcedChannel); connect(m_engine, &QSmf::signalSMFforcedPort, this, &QSpySMF::forcedPort); connect(m_engine, &QSmf::signalSMFText, this, &QSpySMF::textEvent); connect(m_engine, &QSmf::signalSMFendOfTrack, this, &QSpySMF::endOfTrackEvent); connect(m_engine, &QSmf::signalSMFTimeSig, this, &QSpySMF::timeSigEvent); connect(m_engine, &QSmf::signalSMFSmpte, this, &QSpySMF::smpteEvent); connect(m_engine, &QSmf::signalSMFKeySig, this, &QSpySMF::keySigEvent); connect(m_engine, &QSmf::signalSMFTempo, this, &QSpySMF::tempoEvent); connect(m_engine, &QSmf::signalSMFError, this, &QSpySMF::errorHandler); cout.setRealNumberNotation(QTextStream::FixedNotation); cout.setRealNumberPrecision(4); #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) cout.setCodec(codec); #else cout.setEncoding(QStringConverter::Utf8); #endif } void QSpySMF::dump(const QString& chan, const QString& event, const QString& data) { cout << right << qSetFieldWidth(7) << m_engine->getCurrentTime(); cout << right << qSetFieldWidth(10) << m_engine->getRealTime() / 1600.0; cout << qSetFieldWidth(3) << chan; cout << qSetFieldWidth(0) << left << " "; cout << qSetFieldWidth(15) << event; cout << qSetFieldWidth(0) << " " << data << endl; } void QSpySMF::dumpStr(const QString& event, const QString& data) { cout << right << qSetFieldWidth(7) << m_engine->getCurrentTime(); cout << right << qSetFieldWidth(10) << m_engine->getRealTime() / 1600.0; cout << qSetFieldWidth(3) << "--"; cout << qSetFieldWidth(0) << left << " "; cout << qSetFieldWidth(15) << event; cout << qSetFieldWidth(0) << " " << data << endl; } void QSpySMF::headerEvent(int format, int ntrks, int division) { dumpStr("SMF Header", QString("Format=%1, Tracks=%2, Division=%3") .arg(format).arg(ntrks).arg(division)); } void QSpySMF::trackStartEvent() { m_currentTrack++; dumpStr("Track", QString("Start: %1").arg(m_currentTrack)); } void QSpySMF::trackEndEvent() { dumpStr("Track", QString("End: %1").arg(m_currentTrack)); } void QSpySMF::endOfTrackEvent() { dumpStr("Meta Event", "End Of Track"); } void QSpySMF::noteOnEvent(int chan, int pitch, int vol) { dump(QString("%1").arg(chan, 2), "Note On", QString("%1, %2").arg(pitch).arg(vol)); } void QSpySMF::noteOffEvent(int chan, int pitch, int vol) { dump(QString("%1").arg(chan, 2), "Note Off", QString("%1, %2").arg(pitch).arg(vol)); } void QSpySMF::keyPressEvent(int chan, int pitch, int press) { dump(QString("%1").arg(chan, 2), "Key Pressure", QString("%1, %2").arg(pitch).arg(press)); } void QSpySMF::ctlChangeEvent(int chan, int ctl, int value) { dump(QString("%1").arg(chan, 2), "Control Change", QString("%1, %2").arg(ctl).arg(value)); } void QSpySMF::pitchBendEvent(int chan, int value) { dump(QString("%1").arg(chan, 2), "Pitch Bend", QString::number(value)); } void QSpySMF::programEvent(int chan, int patch) { dump(QString("%1").arg(chan, 2), "Program Change", QString::number(patch)); } void QSpySMF::chanPressEvent(int chan, int press) { dump(QString("%1").arg(chan, 2), "Chan.Pressure", QString::number(press)); } void QSpySMF::sysexEvent(const QByteArray& data) { int j; QString s; for (j = 0; j < data.count(); ++j) s.append(QString("%1 ").arg(data[j] & 0xff, 2, 16)); dumpStr("SysEx", s); } /*void QSpySMF::variableEvent(const QByteArray& data) { int j; QString s; for (j = 0; j < data.count(); ++j) s.append(QString("%1 ").arg(data[j] & 0xff, 2, 16)); dumpStr("Variable event", s); }*/ void QSpySMF::seqSpecificEvent(const QByteArray& data) { int j; QString s; for (j = 0; j < data.count(); ++j) s.append(QString("%1 ").arg(data[j] & 0xff, 2, 16)); dumpStr("Seq. specific", s); } void QSpySMF::metaMiscEvent(int typ, const QByteArray& data) { int j; QString s = QString("type=%1 ").arg(typ); for (j = 0; j < data.count(); ++j) s.append(QString("%1 ").arg(data[j] & 0xff, 2, 16)); dumpStr("Meta (unreg.)", s); } void QSpySMF::seqNum(int seq) { dump("--", "Sequence num.", QString::number(seq)); } void QSpySMF::forcedChannel(int channel) { dump("--", "Forced channel", QString::number(channel)); } void QSpySMF::forcedPort(int port) { dump("--", "Forced port", QString::number(port)); } void QSpySMF::textEvent(int typ, const QString& data) { dumpStr(QString("Text (%1)").arg(typ), data); } void QSpySMF::smpteEvent(int b0, int b1, int b2, int b3, int b4) { dump("--", "SMPTE", QString("%1, %2, %3, %4, %5").arg(b0).arg(b1).arg(b2).arg(b3).arg(b4)); } void QSpySMF::timeSigEvent(int b0, int b1, int b2, int b3) { dump("--", "Time Signature", QString("%1, %2, %3, %4").arg(b0).arg(b1).arg(b2).arg(b3)); } void QSpySMF::keySigEvent(int b0, int b1) { dump("--", "Key Signature", QString("%1, %2").arg(b0).arg(b1)); } void QSpySMF::tempoEvent(int tempo) { dump("--", "Tempo", QString::number(tempo)); } void QSpySMF::errorHandler(const QString& errorStr) { m_rc++; cerr << "*** Warning! " << errorStr << " at file offset " << m_engine->getFilePos() << endl; } void QSpySMF::run(QString fileName) { m_currentTrack = 0; cout << "__ticks __seconds ch event__________ data____" << endl; m_engine->readFromFile(fileName); } int QSpySMF::numErrors() { return m_rc; } int main(int argc, char **argv) { const QString PGM_NAME = QStringLiteral("drumstick-dumpsmf"); const QString PGM_DESCRIPTION = QStringLiteral("Drumstick command line utility for decoding SMF (Standard MIDI) files"); QSpySMF spy; QCoreApplication app(argc, argv); QCoreApplication::setApplicationName(PGM_NAME); QCoreApplication::setApplicationVersion(QStringLiteral(QT_STRINGIFY(VERSION))); QCommandLineParser parser; parser.setApplicationDescription(PGM_DESCRIPTION); auto helpOption = parser.addHelpOption(); auto versionOption = parser.addVersionOption(); parser.addPositionalArgument("file", "Input SMF file name.", "files..."); parser.process(app); if (parser.isSet(versionOption) || parser.isSet(helpOption)) { return 0; } QStringList fileNames, positionalArgs = parser.positionalArguments(); if (positionalArgs.isEmpty()) { cerr << "Input file name(s) missing" << endl; parser.showHelp(); } foreach(const QString& a, positionalArgs) { QFileInfo f(a); if (f.exists()) fileNames += f.canonicalFilePath(); else cerr << "File not found: " << a << endl; } foreach(const QString& file, fileNames) { spy.run(file); } return spy.numErrors(); } DISABLE_WARNING_POP drumstick-2.9.0/utils/dumpsmf/dumpsmf.pro0000644000175000017500000000105414541630232017543 0ustar pedropedroTEMPLATE = app TARGET = drumstick-dumpsmf CONFIG += c++11 cmdline qt equals(QT_MAJOR_VERSION, 6) { QT += core5compat } static { CONFIG += link_prl DEFINES += DRUMSTICK_STATIC } DESTDIR = ../../build/bin INCLUDEPATH += . ../../library/include include (../../global.pri) # Input HEADERS += dumpsmf.h SOURCES += dumpsmf.cpp macx:!static { QMAKE_LFLAGS += -F$$OUT_PWD/../../build/lib -L$$OUT_PWD/../../build/lib LIBS += -framework drumstick-file } else { LIBS = -L$$OUT_PWD/../../build/lib \ -l$$drumstickLib(drumstick-file) } drumstick-2.9.0/utils/dumpsmf/CMakeLists.txt0000644000175000017500000000276014541630232020113 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(dumpsmf_SRCS dumpsmf.cpp dumpsmf.h ) set(dumpsmf_qtobject_SRCS dumpsmf.h ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(dumpsmf_moc_SRCS ${dumpsmf_qtobject_SRCS}) else() qt_wrap_cpp(dumpsmf_moc_SRCS ${dumpsmf_qtobject_SRCS}) endif() add_executable(drumstick-dumpsmf ${dumpsmf_moc_SRCS} ${dumpsmf_SRCS} ) target_link_libraries(drumstick-dumpsmf PRIVATE Drumstick::File Qt${QT_VERSION_MAJOR}::Core ) if(QT_VERSION VERSION_GREATER_EQUAL 6.0.0) target_link_libraries(drumstick-dumpsmf PRIVATE Qt6::Core5Compat) endif() install(TARGETS drumstick-dumpsmf RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) drumstick-2.9.0/utils/dumpsmf/dumpsmf.h0000644000175000017500000000442114541630232017173 0ustar pedropedro/* Standard MIDI File dump program Copyright (C) 2006-2023, Pedro Lopez-Cabanillas Based on midifile.c by Tim Thompson, M.Czeiszperger and Greg Lee This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include class QSpySMF : public QObject { Q_OBJECT public: QSpySMF(); void run(QString fileName); void dump(const QString& chan, const QString& event, const QString& data); void dumpStr(const QString& event, const QString& data); int numErrors(); public Q_SLOTS: void headerEvent(int format, int ntrks, int division); void trackStartEvent(); void trackEndEvent(); void endOfTrackEvent(); void noteOnEvent(int chan, int pitch, int vol); void noteOffEvent(int chan, int pitch, int vol); void keyPressEvent(int chan, int pitch, int press); void ctlChangeEvent(int chan, int ctl, int value); void pitchBendEvent(int chan, int value); void programEvent(int chan, int patch); void chanPressEvent(int chan, int press); void sysexEvent(const QByteArray& data); //void variableEvent(const QByteArray& data); void seqSpecificEvent(const QByteArray& data); void metaMiscEvent(int typ, const QByteArray& data); void seqNum(int seq); void forcedChannel(int channel); void forcedPort(int port); void textEvent(int typ, const QString& data); void smpteEvent(int b0, int b1, int b2, int b3, int b4); void timeSigEvent(int b0, int b1, int b2, int b3); void keySigEvent(int b0, int b1); void tempoEvent(int tempo); void errorHandler(const QString& errorStr); private: int m_currentTrack; drumstick::File::QSmf *m_engine; int m_rc; }; drumstick-2.9.0/utils/dumpwrk/0000755000175000017500000000000014541630232015364 5ustar pedropedrodrumstick-2.9.0/utils/dumpwrk/dumpwrk.cpp0000644000175000017500000004056614541630232017574 0ustar pedropedro/* Cakewalk WRK file dump program Copyright (C) 2006-2023, Pedro Lopez-Cabanillas Based on midifile.c by Tim Thompson, M.Czeiszperger and Greg Lee This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "dumpwrk.h" #include #include #include #include #include #include #include #include #include #include #include DISABLE_WARNING_PUSH DISABLE_WARNING_DEPRECATED_DECLARATIONS #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) #define right Qt::right #define left Qt::left #define endl Qt::endl #endif QTextStream cout(stdout, QIODevice::WriteOnly); QTextStream cerr(stderr, QIODevice::WriteOnly); using drumstick::File::QWrk; const QString QSpyWrk::NO_CHANNEL = QStringLiteral("--"); QSpyWrk::QSpyWrk(): m_verbosity(false), m_engine(nullptr), m_rc(0) { m_engine = new QWrk(this); m_engine->setTextCodec(QTextCodec::codecForName("Windows-1252")); connect(m_engine, &QWrk::signalWRKError, this, &QSpyWrk::errorHandler); connect(m_engine, &QWrk::signalWRKUnknownChunk, this, &QSpyWrk::unknownChunk); connect(m_engine, &QWrk::signalWRKHeader, this, &QSpyWrk::fileHeader); connect(m_engine, &QWrk::signalWRKGlobalVars, this, &QSpyWrk::globalVars); connect(m_engine, &QWrk::signalWRKTrack, this, &QSpyWrk::trackHeader); connect(m_engine, &QWrk::signalWRKTimeBase, this, &QSpyWrk::timeBase); connect(m_engine, &QWrk::signalWRKNote, this, &QSpyWrk::noteEvent); connect(m_engine, &QWrk::signalWRKKeyPress, this, &QSpyWrk::keyPressEvent); connect(m_engine, &QWrk::signalWRKCtlChange, this, &QSpyWrk::ctlChangeEvent); connect(m_engine, &QWrk::signalWRKPitchBend, this, &QSpyWrk::pitchBendEvent); connect(m_engine, &QWrk::signalWRKProgram, this, &QSpyWrk::programEvent); connect(m_engine, &QWrk::signalWRKChanPress, this, &QSpyWrk::chanPressEvent); connect(m_engine, &QWrk::signalWRKSysexEvent, this, &QSpyWrk::sysexEvent); connect(m_engine, &QWrk::signalWRKSysex, this, &QSpyWrk::sysexEventBank); connect(m_engine, &QWrk::signalWRKText, this, &QSpyWrk::textEvent); connect(m_engine, &QWrk::signalWRKTimeSig, this, &QSpyWrk::timeSigEvent); connect(m_engine, &QWrk::signalWRKKeySig, this, &QSpyWrk::keySigEvent); connect(m_engine, &QWrk::signalWRKTempo, this, &QSpyWrk::tempoEvent); connect(m_engine, &QWrk::signalWRKThru, this, &QSpyWrk::thruMode); connect(m_engine, &QWrk::signalWRKTrackOffset, this, &QSpyWrk::trackOffset); connect(m_engine, &QWrk::signalWRKTrackReps, this, &QSpyWrk::trackReps); connect(m_engine, &QWrk::signalWRKTrackPatch, this, &QSpyWrk::trackPatch); connect(m_engine, &QWrk::signalWRKTimeFormat, this, &QSpyWrk::timeFormat); connect(m_engine, &QWrk::signalWRKComments, this, &QSpyWrk::comments); connect(m_engine, &QWrk::signalWRKVariableRecord, this, &QSpyWrk::variableRecord); connect(m_engine, &QWrk::signalWRKNewTrack, this, &QSpyWrk::newTrackHeader); connect(m_engine, &QWrk::signalWRKSoftVer, this, &QSpyWrk::softVersion); connect(m_engine, &QWrk::signalWRKTrackName, this, &QSpyWrk::trackName); connect(m_engine, &QWrk::signalWRKStringTable, this, &QSpyWrk::stringTable); connect(m_engine, &QWrk::signalWRKTrackVol, this, &QSpyWrk::trackVol); connect(m_engine, &QWrk::signalWRKTrackBank, this, &QSpyWrk::trackBank); connect(m_engine, &QWrk::signalWRKSegment, this, &QSpyWrk::segment); connect(m_engine, &QWrk::signalWRKChord, this, &QSpyWrk::chord); connect(m_engine, &QWrk::signalWRKExpression, this, &QSpyWrk::expression); connect(m_engine, &QWrk::signalWRKHairpin, this, &QSpyWrk::hairpin); connect(m_engine, &QWrk::signalWRKMarker, this, &QSpyWrk::marker); cout.setRealNumberNotation(QTextStream::FixedNotation); cout.setRealNumberPrecision(4); #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) cout.setCodec("UTF-8"); #else cout.setEncoding(QStringConverter::Utf8); #endif } void QSpyWrk::dump(const long time, const int track, const QString& chan, const QString& event, const QString& data) { cout << right << qSetFieldWidth(7) << time; cout << qSetFieldWidth(0) << left << ' '; cout << qSetFieldWidth(5) << right << track; cout << qSetFieldWidth(3) << chan; cout << qSetFieldWidth(0) << left << ' '; cout << qSetFieldWidth(25) << event; cout << qSetFieldWidth(0) << ' ' << data << endl; } void QSpyWrk::dumpStr(const long time, const QString& event, const QString& data) { cout << right << qSetFieldWidth(7) << time; cout << qSetFieldWidth(6) << NO_CHANNEL; cout << qSetFieldWidth(3) << NO_CHANNEL; cout << qSetFieldWidth(0) << left << ' '; cout << qSetFieldWidth(25) << event; cout << qSetFieldWidth(0) << ' ' << data << endl; } void QSpyWrk::dumpHex(const QByteArray& data) { int i = 0, j = 0; QString s; if (m_verbosity) { while ( i < data.count() ) { s.clear(); for (j = 0; j < 16 && i < data.count(); ++j) { quint8 c = static_cast(data[i++]); s += QString(" %1").arg(c & 0xff, 2, 16, QChar('0')); } cout << qSetFieldWidth(42) << ' ' << qSetFieldWidth(0) << s << endl; } } } void QSpyWrk::dumpVar(const QString& name, int value) { cout << qSetFieldWidth(43) << ' ' << qSetFieldWidth(0) << name << " = " << value << endl; } void QSpyWrk::dumpVar(const QString& name, unsigned int value) { cout << qSetFieldWidth(43) << ' ' << qSetFieldWidth(0) << name << " = " << value << endl; } void QSpyWrk::dumpVar(const QString& name, bool value) { cout << qSetFieldWidth(43) << ' ' << qSetFieldWidth(0) << name << " = " << (value ? "true" : "false" ) << endl; } void QSpyWrk::setVerbosity(bool enabled) { m_verbosity = enabled; } bool QSpyWrk::verbosityEnabled() const { return m_verbosity; } void QSpyWrk::errorHandler(const QString& errorStr) { m_rc++; cerr << "*** Warning! " << errorStr << endl; } void QSpyWrk::fileHeader(int verh, int verl) { dumpStr(0, "WRK File Version", QString("%1.%2").arg(verh).arg(verl)); } void QSpyWrk::globalVars() { dumpStr(0, "Global Vars", QString()); if (m_verbosity) { dumpVar("Now", m_engine->getNow()); dumpVar("From", m_engine->getFrom()); dumpVar("Thru", m_engine->getThru()); dumpVar("KeySig", m_engine->getKeySig()); dumpVar("Clock", m_engine->getClock()); dumpVar("AutoSave", m_engine->getAutoSave()); dumpVar("PlayDelay", m_engine->getPlayDelay()); dumpVar("ZeroCtrls", m_engine->getZeroCtrls()); dumpVar("SendSPP", m_engine->getSendSPP()); dumpVar("SendCont", m_engine->getSendCont()); dumpVar("PatchSearch", m_engine->getPatchSearch()); dumpVar("AutoStop", m_engine->getAutoStop()); dumpVar("StopTime", m_engine->getStopTime()); dumpVar("AutoRewind", m_engine->getAutoRewind()); dumpVar("RewindTime", m_engine->getRewindTime()); dumpVar("MetroPlay", m_engine->getMetroPlay()); dumpVar("MetroRecord", m_engine->getMetroRecord()); dumpVar("MetroAccent", m_engine->getMetroAccent()); dumpVar("CountIn", m_engine->getCountIn()); dumpVar("ThruOn", m_engine->getThruOn()); dumpVar("AutoRestart", m_engine->getAutoRestart()); dumpVar("CurTempoOfs", m_engine->getCurTempoOfs()); dumpVar("TempoOfs1", m_engine->getTempoOfs1()); dumpVar("TempoOfs2", m_engine->getTempoOfs2()); dumpVar("TempoOfs3", m_engine->getTempoOfs3()); dumpVar("PunchEnabled", m_engine->getPunchEnabled()); dumpVar("PunchInTime", m_engine->getPunchInTime()); dumpVar("PunchOutTime", m_engine->getPunchOutTime()); dumpVar("EndAllTime", m_engine->getEndAllTime()); } } void QSpyWrk::trackHeader( const QString& name1, const QString& name2, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop ) { dump(0, trackno, QString::number(channel), "Track", QString("name1='%2' name2='%3'").arg(name1, name2)); if (m_verbosity) { dumpVar("pitch", pitch); dumpVar("velocity",velocity); dumpVar("port", port); dumpVar("selected", selected); dumpVar("muted", muted); dumpVar("loop", loop); } } void QSpyWrk::timeBase(int timebase) { dumpStr(0, "Ticks per Quarter Note", QString::number(timebase)); } void QSpyWrk::noteEvent(int track, long time, int chan, int pitch, int vol, int dur) { dump(time, track, QString::number(chan), "Note", QString("key=%1 vel=%2 dur=%3").arg(pitch).arg(vol).arg(dur)); } void QSpyWrk::keyPressEvent(int track, long time, int chan, int pitch, int press) { dump(time, track, QString::number(chan), "Key Pressure", QString("key=%1 press=%2").arg(pitch).arg(press)); } void QSpyWrk::ctlChangeEvent(int track, long time, int chan, int ctl, int value) { dump(time, track, QString::number(chan), "Control Change", QString("ctl=%1 val=%2").arg(ctl).arg(value)); } void QSpyWrk::pitchBendEvent(int track, long time, int chan, int value) { dump(time, track, QString::number(chan), "Pitch Bend", QString::number(value)); } void QSpyWrk::programEvent(int track, long time, int chan, int patch) { dump(time, track, QString::number(chan), "Program Change", QString::number(patch)); } void QSpyWrk::chanPressEvent(int track, long time, int chan, int press) { dump(time, track, QString::number(chan), "Channel Pressure", QString::number(press)); } void QSpyWrk::sysexEvent(int track, long time, int bank) { dump(time, track, NO_CHANNEL, "System Exclusive", QString::number(bank)); } void QSpyWrk::sysexEventBank(int bank, const QString& name, bool autosend, int port, const QByteArray& data) { dumpStr(0, "System Exclusive Bank", QString("bank=%1 name='%2' auto=%3 port=%4").arg(bank).arg(name).arg(autosend).arg(port)); dumpHex(data); } void QSpyWrk::forcedChannel(int channel) { dump(0, 0, NO_CHANNEL, "Forced channel", QString::number(channel)); } void QSpyWrk::forcedPort(int port) { dump(0, 0, NO_CHANNEL, "Forced port", QString::number(port)); } void QSpyWrk::textEvent(int track, long time, int typ, const QString& data) { dump(time, track, NO_CHANNEL, QString("Text (%1)").arg(typ), data); } void QSpyWrk::timeSigEvent(int bar, int num, int den) { dumpStr(0, "Time Signature", QString("bar=%1, %2/%3").arg(bar).arg(num).arg(den)); } void QSpyWrk::keySigEvent(int bar, int alt) { dumpStr(0, "Key Signature", QString("bar=%1, alt=%2").arg(bar).arg(alt)); } void QSpyWrk::tempoEvent(long time, int tempo) { double bpm = tempo / 100.0; dumpStr(time, "Tempo", QString::number(bpm, 'f', 2)); } void QSpyWrk::thruMode(int mode, int port, int channel, int keyPlus, int velPlus, int localPort) { dumpStr(0, "Thru Mode", QString("mode=%1 port=%2 chan=%3 key+=%4 vel+=%5 port=%6").arg(mode).arg(port).arg(channel).arg(keyPlus).arg(velPlus).arg(localPort)); } void QSpyWrk::trackOffset(int track, int ofs) { dump(0, track, NO_CHANNEL, "Track Offset", QString::number(ofs)); } void QSpyWrk::trackReps(int track, int reps) { dump(0, track, NO_CHANNEL, "Track Repetitions", QString::number(reps)); } void QSpyWrk::trackPatch(int track, int patch) { dump(0, track, NO_CHANNEL, "Track Patch", QString::number(patch)); } void QSpyWrk::timeFormat(int frsec, int ofs) { dumpStr(0, "SMPTE Time Format", QString("%1 frames/second, offset=%2").arg(frsec).arg(ofs)); } void QSpyWrk::comments(const QString& cmt) { dumpStr(0, "Comment", cmt.trimmed()); } void QSpyWrk::variableRecord(const QString& name, const QByteArray& data) { QString s = name; bool isReadable = ( name == "Title" || name == "Author" || name == "Copyright" || name == "Subtitle" || name == "Instructions" || name == "Keywords" ); if (isReadable) { s += ": "; if (m_engine->getTextCodec() == nullptr) s += QString(data); else s += m_engine->getTextCodec()->toUnicode(data); } dumpStr(0, "Variable Record", s.trimmed()); if (!isReadable) dumpHex(data); } void QSpyWrk::newTrackHeader( const QString& name, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop ) { dump(0, trackno, QString::number(channel), "Track", name); if (m_verbosity) { dumpVar("pitch", pitch); dumpVar("velocity",velocity); dumpVar("port", port); dumpVar("selected", selected); dumpVar("muted", muted); dumpVar("loop", loop); } } void QSpyWrk::softVersion(const QString& version) { dumpStr(0, "Software Version", version); } void QSpyWrk::trackName(int trackno, const QString& name) { dump(0, trackno, NO_CHANNEL, "Track Name", name); } void QSpyWrk::stringTable(const QStringList& table) { dumpStr(0, "String Table", table.join(", ")); } void QSpyWrk::trackVol(int track, int vol) { dump(0, track, NO_CHANNEL, "Track Volume", QString::number(vol)); } void QSpyWrk::trackBank(int track, int bank) { dump(0, track, NO_CHANNEL, "Track Bank", QString::number(bank)); } void QSpyWrk::segment(int track, long time, const QString& name) { dump(time, track, NO_CHANNEL, "Track Segment", name); } void QSpyWrk::chord(int track, long time, const QString& name, const QByteArray& data) { dump(time, track, NO_CHANNEL, "Chord Diagram", name); dumpHex(data); } void QSpyWrk::expression(int track, long time, int code, const QString& text) { dump(time, track, NO_CHANNEL, "Expression", QString("text=%1 code=%2").arg(text).arg(code)); } void QSpyWrk::hairpin(int track, long time, int code, int dur) { dump(time, track, NO_CHANNEL, "Hairpin", QString("code=%1, dur=%2").arg(code).arg(dur)); } void QSpyWrk::marker(long time, int smpte, const QString &text) { dumpStr(time, smpte == 0 ? "Marker" : "SMPTE Marker", text); } void QSpyWrk::unknownChunk(int type, const QByteArray& data) { QString name = QString("Unknown Chunk %1 (0x%2)").arg(type).arg(type, 2, 16, QChar('0')); dumpStr(0, name, QString("size=%2").arg(data.length())); dumpHex(data); } void QSpyWrk::run(QString fileName) { cout << "__ticks track ch event____________________ data____" << endl; m_engine->readFromFile(fileName); } int main(int argc, char *argv[]) { const QString PGM_NAME = QStringLiteral("drumstick-dumpwrk"); const QString PGM_DESCRIPTION = QStringLiteral("Drumstick command line utility for decoding WRK (Cakewalk) files"); QSpyWrk spy; QCoreApplication app(argc, argv); QCoreApplication::setApplicationName(PGM_NAME); QCoreApplication::setApplicationVersion(QStringLiteral(QT_STRINGIFY(VERSION))); QCommandLineParser parser; parser.setApplicationDescription(PGM_DESCRIPTION); auto helpOption = parser.addHelpOption(); auto versionOption = parser.addVersionOption(); QCommandLineOption verboseOption("verbose", "Verbose output."); parser.addOption(verboseOption); parser.addPositionalArgument("file", "Input WRK File Name(s).", "files..."); parser.process(app); if (parser.isSet(versionOption) || parser.isSet(helpOption)) { return 0; } if (parser.isSet(verboseOption)) { spy.setVerbosity(true); } QStringList fileNames, positionalArgs = parser.positionalArguments(); foreach(const QVariant& a, positionalArgs) { QFileInfo f(a.toString()); if (f.exists()) fileNames += f.canonicalFilePath(); else cerr << "File not found: " << a.toString() << endl; } foreach(const QString& file, fileNames) { spy.run(file); } return spy.returnCode(); } DISABLE_WARNING_POP drumstick-2.9.0/utils/dumpwrk/dumpwrk.pro0000644000175000017500000000105414541630232017577 0ustar pedropedroTEMPLATE = app TARGET = drumstick-dumpwrk CONFIG += c++11 cmdline qt equals(QT_MAJOR_VERSION, 6) { QT += core5compat } static { CONFIG += link_prl DEFINES += DRUMSTICK_STATIC } DESTDIR = ../../build/bin INCLUDEPATH += . ../../library/include include (../../global.pri) # Input HEADERS += dumpwrk.h SOURCES += dumpwrk.cpp macx:!static { QMAKE_LFLAGS += -F$$OUT_PWD/../../build/lib -L$$OUT_PWD/../../build/lib LIBS += -framework drumstick-file } else { LIBS = -L$$OUT_PWD/../../build/lib \ -l$$drumstickLib(drumstick-file) } drumstick-2.9.0/utils/dumpwrk/CMakeLists.txt0000644000175000017500000000276014541630232020131 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(dumpwrk_SRCS dumpwrk.cpp dumpwrk.h ) set(dumpwrk_qtobject_SRCS dumpwrk.h ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(dumpwrk_moc_SRCS ${dumpwrk_qtobject_SRCS}) else() qt_wrap_cpp(dumpwrk_moc_SRCS ${dumpwrk_qtobject_SRCS}) endif() add_executable(drumstick-dumpwrk ${dumpwrk_moc_SRCS} ${dumpwrk_SRCS} ) target_link_libraries(drumstick-dumpwrk PRIVATE Drumstick::File Qt${QT_VERSION_MAJOR}::Core ) if(QT_VERSION VERSION_GREATER_EQUAL 6.0.0) target_link_libraries(drumstick-dumpwrk PRIVATE Qt6::Core5Compat) endif() install(TARGETS drumstick-dumpwrk RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) drumstick-2.9.0/utils/dumpwrk/dumpwrk.h0000644000175000017500000000775614541630232017245 0ustar pedropedro/* Cakewalk WRK file dump program Copyright (C) 2006-2023, Pedro Lopez-Cabanillas Based on midifile.c by Tim Thompson, M.Czeiszperger and Greg Lee This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include class QSpyWrk : public QObject { Q_OBJECT public: QSpyWrk(); void run(QString fileName); void dump(const long time, const int track, const QString& chan, const QString& event, const QString& data); void dumpStr(const long time, const QString& event, const QString& data); void dumpHex(const QByteArray& data); void dumpVar(const QString& name, bool value); void dumpVar(const QString& name, int value); void dumpVar(const QString& name, unsigned int value); void setVerbosity(bool enable); bool verbosityEnabled() const; int returnCode() const { return m_rc; } static const QString NO_CHANNEL; public Q_SLOTS: void unknownChunk(int type, const QByteArray& data); void fileHeader(int verh, int verl); void trackHeader(const QString& name1, const QString& name2, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop); void timeBase(int timebase); void globalVars(); void noteEvent(int track, long time, int chan, int pitch, int vol, int dur); void keyPressEvent(int track, long time, int chan, int pitch, int press); void ctlChangeEvent(int track, long time, int chan, int ctl, int value); void pitchBendEvent(int track, long time, int chan, int value); void programEvent(int track, long time, int chan, int patch); void chanPressEvent(int track, long time, int chan, int press); void sysexEvent(int track, long time, int bank); void sysexEventBank(int bank, const QString& name, bool autosend, int port, const QByteArray& data); void forcedChannel(int channel); void forcedPort(int port); void textEvent(int track, long time, int typ, const QString& data); void timeSigEvent(int bar, int num, int den); void keySigEvent(int bar, int alt); void tempoEvent(long time, int tempo); void errorHandler(const QString& errorStr); void thruMode(int mode, int port, int channel, int keyPlus, int velPlus, int localPort); void trackOffset(int track, int ofs); void trackReps(int track, int reps); void trackPatch(int track, int patch); void timeFormat(int frsec, int ofs); void comments(const QString& cmt); void variableRecord(const QString& name, const QByteArray& data); void newTrackHeader(const QString& name, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop); void softVersion(const QString& version); void trackName(int trackno, const QString& name); void stringTable(const QStringList& table); void trackVol(int track, int vol); void trackBank(int track, int bank); void segment(int track, long time, const QString& name); void chord(int track, long time, const QString& name, const QByteArray& data); void expression(int track, long time, int code, const QString& text); void hairpin(int track, long time, int code, int dur); void marker(long time, int smpte, const QString& text); private: bool m_verbosity; drumstick::File::QWrk *m_engine; int m_rc; }; drumstick-2.9.0/utils/guiplayer/0000755000175000017500000000000014541630232015674 5ustar pedropedrodrumstick-2.9.0/utils/guiplayer/drumstick-guiplayer.desktop0000644000175000017500000000047114541630232023275 0ustar pedropedro[Desktop Entry] Name=Drumstick ALSA MIDI Player Exec=drumstick-guiplayer %f Icon=drumstick Terminal=false Type=Application Categories=AudioVideo;Audio;Midi;Education;Music; Keywords=Music;Midi;Player; MimeType=audio/midi;audio/x-midi;audio/cakewalk; Comment=Simple MIDI Player Comment[es]=Reproductor MIDI simple drumstick-2.9.0/utils/guiplayer/drumstick-guiplayer_cs.ts0000644000175000017500000004654514541630232022753 0ustar pedropedro About Revision Verze AboutClass About O programu <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick ALSA MIDI Player %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick ALSA MIDI Player %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> About Qt O Qt GUIPlayerClass Drumstick ALSA MIDI Player Drumstick MIDI Player Přehrávač MIDI Palička Playback time and current levels Čas přehrávání a nynější úrovně 00:00:00 00:00:00 Tempo: Tempo: Volume: Hlasitost: Pitch: Výška tónu: Pitch Control Ovládání výšky tónu Pitch transpose between -12 and +12 semitones Transpozice výšky tónu mezi -12 a +12 půltóny Reset Volume Obnovit výchozí hlasitost Reset Volume to 100% Obnovit výchozí hlasitost na 100 % Volume Slider Posuvník hlasitosti Playback progress Postup přehrávání File Name: Název souboru: Currently loaded MIDI file name Nyní nahraný název souboru MIDI Tempo Slider Posuvník tempa Reset Tempo Obnovit výchozí tempo Reset Tempo to 100% Obnovit výchozí tempo na 100 % tempo=100% Tempo = 100 % File Soubor Help Nápověda Settings Nastavení Tool Bar Nástrojový pruh Open Otevřít Open a MIDI file Otevřít soubor MIDI Quit Ukončit Quit the application Ukončit program Play Přehrát Start playing the current MIDI file Spustit přehrávání nynějšího souboru MIDI Pause Pozastavit Pause the playback Pozastavit přehrávání Stop Zastavit Stop the playback Zastavit přehrávání About O programu Show the about box Ukázat okno O programu About Qt O Qt Show the about Qt dialog box Ukázat okno O Qt MIDI Setup Nastavení MIDI Select a connection for the MIDI output port Vybrat spojení pro výstupní přípojku MIDI Show Tool Bar Ukázat nástrojový pruh Show or hide the tool bar Ukázat nebo skrýt nástrojový pruh Show Status Bar Ukázat stavový řádek Show or hide the status bar Ukázat nebo skrýt stavový řádek main ALSA Sequencer based MIDI file player Fatal error from the ALSA sequencer. This usually happens when the kernel doesn't have ALSA support, or the device node (/dev/snd/seq) doesn't exists, or the kernel module (snd_seq) is not loaded. Please check your ALSA/MIDI configuration. MIDI Out Connection. Input SMF/KAR/RMI/WRK file name. File not found: Error Returned error was: drumstick-2.9.0/utils/guiplayer/drumstick-guiplayer_en.ts0000644000175000017500000004335314541630232022742 0ustar pedropedro About Revision AboutClass About <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick ALSA MIDI Player %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick ALSA MIDI Player %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> About Qt GUIPlayerClass Drumstick ALSA MIDI Player Drumstick MIDI Player Playback time and current levels 00:00:00 Tempo: Volume: Pitch: Pitch Control Pitch transpose between -12 and +12 semitones Reset Volume Reset Volume to 100% Volume Slider Playback progress File Name: Currently loaded MIDI file name Tempo Slider Reset Tempo Reset Tempo to 100% tempo=100% File Help Settings Tool Bar Open Open a MIDI file Quit Quit the application Play Start playing the current MIDI file Pause Pause the playback Stop Stop the playback About Show the about box About Qt Show the about Qt dialog box MIDI Setup Select a connection for the MIDI output port Show Tool Bar Show or hide the tool bar Show Status Bar Show or hide the status bar drumstick-2.9.0/utils/guiplayer/drumstick-guiplayer_es.ts0000644000175000017500000006040714541630232022746 0ustar pedropedro About Revision Revisión AboutClass About Acerca de <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick ALSA MIDI Player %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick ALSA MIDI Player %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick MIDI Player %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> About Qt Acerca de Qt GUIPlayerClass Drumstick ALSA MIDI Player Reproductor ALSA MIDI Drumstick Drumstick MIDI Player Reproductor MIDI Drumstick Playback time and current levels Tiempo de reproducción y niveles actuales 00:00:00 00:00:00 Tempo: Tempo: Volume: Volumen: Pitch: Transporte: Pitch Control Control de tono Pitch transpose between -12 and +12 semitones Transporte del tono entre -12 y +12 semitonos Reset Volume Restablecer volumen Reset Volume to 100% Restablecer volumen al 100% Volume Slider Deslizador de volumen Playback progress Progreso de reproducción File Name: Nombre de archivo: Currently loaded MIDI file name Nombre de archivo MIDI actual Tempo Slider Deslizador de tempo Reset Tempo Restablecer tempo Reset Tempo to 100% Restablecer el tempo al 100% tempo=100% tempo=100% File Archivo Help Ayuda Settings Preferencias Tool Bar Barra de herramientas Open Abrir Open a MIDI file Abrir un archivo MIDI Quit Terminar Quit the application Terminar la aplicación Play Reproducir Start playing the current MIDI file Iniciar la reproducción del archivo MIDI actual Pause Pausa Pause the playback Pausar la reproducción Stop Parar Stop the playback Parar la reproducción About Acerca de Show the about box Mostrar el cuadro de Acerca de About Qt Acerca de Qt Show the about Qt dialog box Mostrar el cuadro de Acerca de Qt MIDI Setup Ajustes MIDI Select a connection for the MIDI output port Seleccionar una conexión para el puerto de salida MIDI Show Tool Bar Mostrar la barra de herramientas Show or hide the tool bar Mostrar u ocultar la barra de herramientas Show Status Bar Mostrar la barra de estado Show or hide the status bar Mostar u ocultar la barra de estado main ALSA Sequencer based MIDI file player Reproductor de archivos MIDI basado en el secuenciador ALSA Fatal error from the ALSA sequencer. This usually happens when the kernel doesn't have ALSA support, or the device node (/dev/snd/seq) doesn't exists, or the kernel module (snd_seq) is not loaded. Please check your ALSA/MIDI configuration. Error fatal en el secuenciador ALSA. Esto ocurre habitualmente cuando el núcleo no tiene soporte ALSA, o bién el nodo de dispositivo (/dev/snd/seq) no existe, o el módulo del núcleo (snd_seq) no se ha cargado. Por favor compruebe su configuración de MIDI ALSA. MIDI Out Connection. Conexión MIDI Out. Input SMF/KAR/RMI/WRK file name. Archivo de entrada SMF/KAR/RMI/WRK. File not found: Archivo no encontrado: Error Error Returned error was: El error devuelto fué: drumstick-2.9.0/utils/guiplayer/drumstick-guiplayer_it.ts0000644000175000017500000005473614541630232022763 0ustar pedropedro About Revision Revisione AboutClass About Informazioni <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick ALSA MIDI Player %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick ALSA MIDI Player %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Drum Grid %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Versione Qt: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Applicazione di esempio per la </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">libreria C++ Drumstick MIDI Sequencer</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">Questo programma è free software; lo si può ridistribuire e/o modificare in base ai termini della GNU General Public License come pubblicata dalla Free Software Foundation; sia nella versione 3 della Licenza, o (a scelta) in qualsiasi versione successiva.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">Questo programma è distribuito con la speranza che sia utile, ma SENZA QUALUNQUE GARANZIA; inclusa l'implicita garanzia di COMMERCIABILITÀ o di ADEGUATEZZA PER UNO SCOPO PARTICOLARE. Vedere la GNU General Public License per ulteriori dettagli.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">Dovreste avere ricevuto una copia della GNU General Public License insieme con questo programma. In caso contrario, vedere </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> About Qt Informazioni su Qt GUIPlayerClass Drumstick ALSA MIDI Player Riproduttore MIDI per ALSA basato su Drumstick Drumstick MIDI Player Riproduttore MIDI Playback time and current levels Tempo di riproduzione e livelli correnti 00:00:00 00:00:00 Tempo: Tempo: Volume: Volume: Pitch: Tonalità: Pitch Control Controllo tonalità Pitch transpose between -12 and +12 semitones Traspone la tonalità fra -12 e +12 semitoni Reset Volume Ripristina volume Reset Volume to 100% Riporta il volume al 100% Volume Slider Cursore del volume Playback progress Progresso della riproduzione File Name: Nome del file: Currently loaded MIDI file name Nome del file MIDI correntemente caricato Tempo Slider Cursore del Tempo Reset Tempo Ripristina Tempo Reset Tempo to 100% Riporta il Tempo al 100% tempo=100% Tempo=100% File File Help Aiuto Settings Impostazioni Tool Bar Barra degli strumenti Open Apri Open a MIDI file Apre un file MIDI Quit Esci Quit the application Esce dall'applicazione Play Riproduci Start playing the current MIDI file Riproduce il file MIDI corrente Pause Pausa Pause the playback Sospende la riproduzione Stop Arresto Stop the playback Arresta la riproduzione About Informazioni Show the about box Mostra la finestra Informazioni About Qt Informazioni su Qt Show the about Qt dialog box Mostra la finestra Informazioni su Qt MIDI Setup Configura MIDI Select a connection for the MIDI output port Seleziona una connessione per la porta MIDI di uscita Show Tool Bar Mostra la barra degli strumenti Show or hide the tool bar Mosta o nasconde la barra degli strumenti Show Status Bar Mostra la barra di stato Show or hide the status bar Mosta o nasconde la barra di stato drumstick-2.9.0/utils/guiplayer/drumstick-guiplayer_ru.ts0000644000175000017500000004761214541630232022770 0ustar pedropedro About Revision AboutClass About О программе <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick ALSA MIDI Player %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick ALSA MIDI Player %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> About Qt О Qt GUIPlayerClass Drumstick ALSA MIDI Player Drumstick MIDI Player Проигрыватель Drumstick MIDI Playback time and current levels Время воспроизведения и текущие уровни 00:00:00 00:00:00 Tempo: Темп: Volume: Громкость: Pitch: Тон: Pitch Control Управление тоном Pitch transpose between -12 and +12 semitones Изменение тона от –12 до +12 полутонов Reset Volume Сбросить громкость Reset Volume to 100% Сбросить громкость на 100% Volume Slider Бегунок громкости Playback progress Прогресс воспроизведения File Name: Имя файла: Currently loaded MIDI file name Имя загруженного MIDI файла Tempo Slider Бегунок темпа Reset Tempo Сбросить темп Reset Tempo to 100% Сбросить темп на 100% tempo=100% темп=100% File Файл Help Справка Settings Настройки Tool Bar Панель инструментов Open Открыть Open a MIDI file Открыть файл MIDI Quit Выход Quit the application Выйти из приложения Play Играть Start playing the current MIDI file Начать воспроизведение текущего файла MIDI Pause Пауза Pause the playback Приостановить воспроизведение Stop Стоп Stop the playback Остановить воспроизведение About О программе Show the about box Показать информацию о программе About Qt О Qt Show the about Qt dialog box Показать информацию о Qt MIDI Setup Настройка MIDI Select a connection for the MIDI output port Выберите соединение для порта выхода MIDI Show Tool Bar Показать панель инструментов Show or hide the tool bar Показать или скрыть панель инструментов Show Status Bar Показать панель статуса Show or hide the status bar Показать или скрыть панель статуса main ALSA Sequencer based MIDI file player Fatal error from the ALSA sequencer. This usually happens when the kernel doesn't have ALSA support, or the device node (/dev/snd/seq) doesn't exists, or the kernel module (snd_seq) is not loaded. Please check your ALSA/MIDI configuration. MIDI Out Connection. Input SMF/KAR/RMI/WRK file name. File not found: Error Returned error was: drumstick-2.9.0/utils/guiplayer/guiplayer.cpp0000644000175000017500000007245314541630232020414 0ustar pedropedro/* SMF GUI Player test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "guiplayer.h" #include "iconutils.h" #include "player.h" #include "playerabout.h" #include "song.h" #include "ui_guiplayer.h" #include #include #include #include #include #include #include #include DISABLE_WARNING_PUSH DISABLE_WARNING_DEPRECATED_DECLARATIONS using namespace drumstick; using namespace ALSA; using namespace File; const QString GUIPlayer::QSTR_DOMAIN = QStringLiteral("drumstick.sourceforge.net"); const QString GUIPlayer::QSTR_APPNAME = QStringLiteral("drumstick-guiplayer"); GUIPlayer::GUIPlayer(QWidget *parent, Qt::WindowFlags flags) : QMainWindow(parent, flags), m_portId(-1), m_queueId(-1), m_initialTempo(0), m_currentTrack(0), m_tempoFactor(1.0), m_tick(0), m_state(InvalidState), m_smf(nullptr), m_wrk(nullptr), m_Client(nullptr), m_Port(nullptr), m_Queue(nullptr), m_player(nullptr), m_ui(new Ui::GUIPlayerClass), m_pd(nullptr), m_song(new Song) { m_ui->setupUi(this); setAcceptDrops(true); connect(m_ui->actionAbout, &QAction::triggered, this, &GUIPlayer::about); connect(m_ui->actionAboutQt, &QAction::triggered, qApp, QApplication::aboutQt); connect(m_ui->actionPlay, &QAction::triggered, this, &GUIPlayer::play); connect(m_ui->actionPause, &QAction::triggered, this, &GUIPlayer::pause); connect(m_ui->actionStop, &QAction::triggered, this, &GUIPlayer::stop); connect(m_ui->actionOpen, &QAction::triggered, this, &GUIPlayer::open); connect(m_ui->actionMIDISetup, &QAction::triggered, this, &GUIPlayer::setup); connect(m_ui->actionQuit, &QAction::triggered, this, &GUIPlayer::close); connect(m_ui->btnTempo, &QPushButton::clicked, this, &GUIPlayer::tempoReset); connect(m_ui->btnVolume, &QPushButton::clicked, this, &GUIPlayer::volumeReset); connect(m_ui->sliderTempo, &QSlider::valueChanged, this, &GUIPlayer::tempoSlider); connect(m_ui->volumeSlider, &QSlider::valueChanged, this, &GUIPlayer::volumeSlider); connect(m_ui->spinPitch, QOverload::of(&QSpinBox::valueChanged), this, &GUIPlayer::pitchShift); connect(m_ui->toolBar->toggleViewAction(), &QAction::toggled, m_ui->actionShowToolbar, &QAction::setChecked); m_ui->actionPlay->setIcon(QIcon(IconUtils::GetPixmap(this, ":/resources/play.png"))); m_ui->actionPlay->setShortcut( Qt::Key_MediaPlay ); m_ui->actionStop->setIcon(QIcon(IconUtils::GetPixmap(this, ":/resources/stop.png"))); m_ui->actionStop->setShortcut( Qt::Key_MediaStop ); m_ui->actionPause->setIcon(QIcon(IconUtils::GetPixmap(this, ":/resources/pause.png"))); m_ui->actionMIDISetup->setIcon(QIcon(IconUtils::GetPixmap(this, ":/resources/setup.png"))); m_Client = new MidiClient(this); m_Client->open(); m_Client->setPoolOutput(50); // small size, for near real-time pitchShift m_Client->setClientName("MIDI Player"); connect( m_Client, &MidiClient::eventReceived, this, &GUIPlayer::sequencerEvent, Qt::QueuedConnection ); m_Port = new MidiPort(this); m_Port->attach( m_Client ); m_Port->setPortName("MIDI Player Output Port"); m_Port->setCapability( SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ | SND_SEQ_PORT_CAP_WRITE ); m_Port->setPortType( SND_SEQ_PORT_TYPE_APPLICATION | SND_SEQ_PORT_TYPE_MIDI_GENERIC ); m_Queue = m_Client->createQueue(QSTR_APPNAME); m_queueId = m_Queue->getId(); m_portId = m_Port->getPortId(); m_rmi = new Rmidi(this); connect(m_rmi, &Rmidi::signalRiffData, this, &GUIPlayer::dataHandler); m_smf = new QSmf(this); connect(m_smf, &QSmf::signalSMFHeader, this, &GUIPlayer::smfHeaderEvent); connect(m_smf, &QSmf::signalSMFNoteOn, this, &GUIPlayer::smfNoteOnEvent); connect(m_smf, &QSmf::signalSMFNoteOff, this, &GUIPlayer::smfNoteOffEvent); connect(m_smf, &QSmf::signalSMFKeyPress, this, &GUIPlayer::smfKeyPressEvent); connect(m_smf, &QSmf::signalSMFCtlChange, this, &GUIPlayer::smfCtlChangeEvent); connect(m_smf, &QSmf::signalSMFPitchBend, this, &GUIPlayer::smfPitchBendEvent); connect(m_smf, &QSmf::signalSMFProgram, this, &GUIPlayer::smfProgramEvent); connect(m_smf, &QSmf::signalSMFChanPress, this, &GUIPlayer::smfChanPressEvent); connect(m_smf, &QSmf::signalSMFSysex, this, &GUIPlayer::smfSysexEvent); connect(m_smf, &QSmf::signalSMFText, this, &GUIPlayer::smfUpdateLoadProgress); connect(m_smf, &QSmf::signalSMFTempo, this, &GUIPlayer::smfTempoEvent); connect(m_smf, &QSmf::signalSMFTrackStart, this, &GUIPlayer::smfUpdateLoadProgress); connect(m_smf, &QSmf::signalSMFTrackStart, this, &GUIPlayer::smfTrackStarted); connect(m_smf, &QSmf::signalSMFTrackEnd, this, &GUIPlayer::smfTrackEnded); connect(m_smf, &QSmf::signalSMFendOfTrack, this, &GUIPlayer::smfUpdateLoadProgress); connect(m_smf, &QSmf::signalSMFError, this, &GUIPlayer::smfErrorHandler); m_wrk = new QWrk(this); m_wrk->setTextCodec(QTextCodec::codecForLocale()); connect(m_wrk, &QWrk::signalWRKError, this, &GUIPlayer::wrkErrorHandler); connect(m_wrk, &QWrk::signalWRKUnknownChunk, this, &GUIPlayer::wrkUpdateLoadProgress); connect(m_wrk, &QWrk::signalWRKHeader, this, &GUIPlayer::wrkFileHeader); connect(m_wrk, &QWrk::signalWRKEnd, this, &GUIPlayer::wrkEndOfFile); connect(m_wrk, &QWrk::signalWRKStreamEnd, this, &GUIPlayer::wrkStreamEndEvent); connect(m_wrk, &QWrk::signalWRKGlobalVars, this, &GUIPlayer::wrkUpdateLoadProgress); connect(m_wrk, &QWrk::signalWRKTrack, this, &GUIPlayer::wrkTrackHeader); connect(m_wrk, &QWrk::signalWRKTimeBase, this, &GUIPlayer::wrkTimeBase); connect(m_wrk, &QWrk::signalWRKNote, this, &GUIPlayer::wrkNoteEvent); connect(m_wrk, &QWrk::signalWRKKeyPress, this, &GUIPlayer::wrkKeyPressEvent); connect(m_wrk, &QWrk::signalWRKCtlChange, this, &GUIPlayer::wrkCtlChangeEvent); connect(m_wrk, &QWrk::signalWRKPitchBend, this, &GUIPlayer::wrkPitchBendEvent); connect(m_wrk, &QWrk::signalWRKProgram, this, &GUIPlayer::wrkProgramEvent); connect(m_wrk, &QWrk::signalWRKChanPress, this, &GUIPlayer::wrkChanPressEvent); connect(m_wrk, &QWrk::signalWRKSysexEvent, this, &GUIPlayer::wrkSysexEvent); connect(m_wrk, &QWrk::signalWRKSysex, this, &GUIPlayer::wrkSysexEventBank); connect(m_wrk, &QWrk::signalWRKText, this, &GUIPlayer::wrkUpdateLoadProgress); connect(m_wrk, &QWrk::signalWRKTimeSig, this, &GUIPlayer::wrkUpdateLoadProgress); connect(m_wrk, &QWrk::signalWRKKeySig, this, &GUIPlayer::wrkUpdateLoadProgress); connect(m_wrk, &QWrk::signalWRKTempo, this, &GUIPlayer::wrkTempoEvent); connect(m_wrk, &QWrk::signalWRKTrackPatch, this, &GUIPlayer::wrkTrackPatch); connect(m_wrk, &QWrk::signalWRKComments, this, &GUIPlayer::wrkUpdateLoadProgress); connect(m_wrk, &QWrk::signalWRKVariableRecord, this, &GUIPlayer::wrkUpdateLoadProgress); connect(m_wrk, &QWrk::signalWRKNewTrack, this, &GUIPlayer::wrkNewTrackHeader); connect(m_wrk, &QWrk::signalWRKTrackName, this, &GUIPlayer::wrkUpdateLoadProgress); connect(m_wrk, &QWrk::signalWRKTrackVol, this, &GUIPlayer::wrkTrackVol); connect(m_wrk, &QWrk::signalWRKTrackBank, this, &GUIPlayer::wrkTrackBank); connect(m_wrk, &QWrk::signalWRKSegment, this, &GUIPlayer::wrkUpdateLoadProgress); connect(m_wrk, &QWrk::signalWRKChord, this, &GUIPlayer::wrkUpdateLoadProgress); connect(m_wrk, &QWrk::signalWRKExpression, this, &GUIPlayer::wrkUpdateLoadProgress); m_player = new Player(m_Client, m_portId); connect(m_player, &Player::playbackStopped, this, &GUIPlayer::playerStopped, Qt::QueuedConnection); m_Client->setRealTimeInput(false); m_Client->startSequencerInput(); tempoReset(); volumeReset(); updateState(EmptyState); readSettings(); } GUIPlayer::~GUIPlayer() { m_Client->stopSequencerInput(); m_Port->unsubscribeAll(); m_Port->detach(); m_Client->close(); delete m_player; delete m_ui; delete m_song; } void GUIPlayer::subscribe(const QString& portName) { try { if (!m_subscription.isEmpty()) { m_Port->unsubscribeTo(m_subscription); } m_subscription = portName; m_Port->subscribeTo(m_subscription); } catch (const SequencerError& err) { qWarning() << "SequencerError exception. Error code: " << err.code() << " (" << err.qstrError() << ")"; qWarning() << "Location: " << err.location(); } } void GUIPlayer::updateTimeLabel(int mins, int secs, int cnts) { static QChar fill('0'); QString stime = QString("%1:%2.%3").arg(mins,2,10,fill) .arg(secs,2,10,fill) .arg(cnts,2,10,fill); m_ui->lblTime->setText(stime); } void GUIPlayer::updateState(PlayerState newState) { if (m_state == newState) return; switch (newState) { case EmptyState: m_ui->actionPlay->setEnabled(false); m_ui->actionPause->setEnabled(false); m_ui->actionStop->setEnabled(false); statusBar()->showMessage("Please, load a song"); break; case PlayingState: m_ui->actionPlay->setEnabled(false); m_ui->actionPause->setEnabled(true); m_ui->actionStop->setEnabled(true); statusBar()->showMessage("Playing"); break; case PausedState: m_ui->actionPlay->setEnabled(false); m_ui->actionStop->setEnabled(true); statusBar()->showMessage("Paused"); break; case StoppedState: m_ui->actionPause->setChecked(false); m_ui->actionPause->setEnabled(false); m_ui->actionStop->setEnabled(false); m_ui->actionPlay->setEnabled(true); statusBar()->showMessage("Stopped"); break; default: statusBar()->showMessage("Not initialized"); break; } m_state = newState; } void GUIPlayer::play() { if (!m_song->isEmpty()) { if (m_player->getInitialPosition() == 0) { if (m_initialTempo == 0) return; QueueTempo firstTempo = m_Queue->getTempo(); firstTempo.setPPQ(m_song->getDivision()); firstTempo.setTempo(m_initialTempo); firstTempo.setTempoFactor(m_tempoFactor); m_Queue->setTempo(firstTempo); m_Client->drainOutput(); m_player->sendVolumeEvents(); } m_player->start(); updateState(PlayingState); } } void GUIPlayer::pause() { if (m_state == PlayingState || m_player->isRunning()) { m_player->stop(); m_player->setPosition(m_Queue->getStatus().getTickTime()); updateState(PausedState); } else if (!m_song->isEmpty()) { m_player->start(); updateState(PlayingState); } } void GUIPlayer::stop() { if (m_state == PlayingState || m_state == PausedState || m_player->isRunning()) { m_Queue->stop(); m_Queue->clear(); m_player->stop(); } if (m_initialTempo != 0) songFinished(); else updateState(StoppedState); } void GUIPlayer::progressDialogInit(const QString& type, int max) { m_pd = new QProgressDialog("", "", 0, max, this); m_pd->setWindowTitle(QString("Loading %1 file...").arg(type)); m_pd->setMinimumDuration(1000); m_pd->setValue(0); } void GUIPlayer::progressDialogUpdate(int pos) { if (m_pd != nullptr) { m_pd->setValue(pos); qApp->processEvents(); } } void GUIPlayer::progressDialogClose() { delete m_pd; // set to 0 by QPointer<> } void GUIPlayer::openFile(const QString& fileName) { QFileInfo finfo(fileName); if (finfo.exists()) { m_song->clear(); m_loadingMessages.clear(); m_tick = 0; m_initialTempo = 0; m_currentTrack = 0; try { QString ext = finfo.suffix().toLower(); if (ext == "wrk") { progressDialogInit("Cakewalk", finfo.size()); m_wrk->readFromFile(fileName); } else if (ext == "mid" || ext == "midi" || ext == "kar") { progressDialogInit("MIDI", finfo.size()); m_smf->readFromFile(fileName); } else if (ext == "rmi") { progressDialogInit("RIFF MIDI", finfo.size()); m_rmi->readFromFile(fileName); } progressDialogUpdate(finfo.size()); if (m_song->isEmpty()) { m_ui->lblName->clear(); } else { m_song->sort(); m_player->setSong(m_song); m_ui->lblName->setText(finfo.fileName()); m_lastDirectory = finfo.absolutePath(); } } catch (...) { m_song->clear(); m_ui->lblName->clear(); } progressDialogClose(); if (m_initialTempo == 0) { m_initialTempo = 500000; } updateTimeLabel(0,0,0); updateTempoLabel(6.0e7f / m_initialTempo); m_ui->progressBar->setValue(0); if (!m_loadingMessages.isEmpty()) { m_loadingMessages.insert(0, "Warning, this file may be non-standard or damaged.
"); QMessageBox::warning(this, QSTR_APPNAME, m_loadingMessages); } if (m_song->isEmpty()) updateState(EmptyState); else updateState(StoppedState); } } void GUIPlayer::open() { QString fileName = QFileDialog::getOpenFileName(this, "Open MIDI File", m_lastDirectory, "All files (*.kar *.mid *.midi *rmi *.wrk);;" "Karaoke files (*.kar);;" "MIDI Files (*.mid *.midi);;" "RIFF MIDI Files (*.rmi);;" "Cakewalk files (*.wrk)" ); if (! fileName.isEmpty() ) { stop(); openFile(fileName); } } void GUIPlayer::setup() { bool ok; int current; QStringList items; QListIterator it(m_Client->getAvailableOutputs()); while(it.hasNext()) { PortInfo p = it.next(); items << QString("%1:%2").arg(p.getClientName()).arg(p.getPort()); } current = items.indexOf(m_subscription); QString item = QInputDialog::getItem(this, "Player subscription", "Output port:", items, current, false, &ok); if (ok && !item.isEmpty()) subscribe(item); } void GUIPlayer::songFinished() { m_player->resetPosition(); updateState(StoppedState); } void GUIPlayer::playerStopped() { int portId = m_Port->getPortId(); for (int channel = 0; channel < 16; ++channel) { ControllerEvent ev1(channel, MIDI_CTL_ALL_NOTES_OFF, 0); ev1.setSource(portId); ev1.setSubscribers(); ev1.setDirect(); m_Client->outputDirect(&ev1); ControllerEvent ev2(channel, MIDI_CTL_ALL_SOUNDS_OFF, 0); ev2.setSource(portId); ev2.setSubscribers(); ev2.setDirect(); m_Client->outputDirect(&ev2); } m_Client->drainOutput(); } void GUIPlayer::updateTempoLabel(float ftempo) { QString stempo = QString("%1 bpm").arg(ftempo, 0, 'f', 2); m_ui->lblOther->setText(stempo); } void GUIPlayer::sequencerEvent(SequencerEvent *ev) { if ((ev->getSequencerType() == SND_SEQ_EVENT_ECHO) && (m_tick != 0)){ auto t = ev->getTick(); int pos = 100 * t / m_tick; const snd_seq_real_time_t* rt = m_Queue->getStatus().getRealtime(); int mins = rt->tv_sec / 60; int secs = rt->tv_sec % 60; int cnts = qFloor( rt->tv_nsec / 1.0e7 ); updateTempoLabel(m_Queue->getTempo().getRealBPM()); updateTimeLabel(mins, secs, cnts); m_ui->progressBar->setValue(pos); if (t >= m_tick) { songFinished(); } } delete ev; } void GUIPlayer::dataHandler(const QString &dataType, const QByteArray &data) { if (dataType == "RMID") { QDataStream ds(data); m_smf->readFromStream(&ds); } } void GUIPlayer::pitchShift(int value) { m_player->setPitchShift(value); } void GUIPlayer::tempoReset() { m_ui->sliderTempo->setValue(100); tempoSlider(100); } void GUIPlayer::volumeReset() { m_ui->volumeSlider->setValue(100); volumeSlider(100); } void GUIPlayer::tempoSlider(int value) { m_tempoFactor = (value*value + 100.0*value + 20000.0) / 40000.0; QueueTempo qtempo = m_Queue->getTempo(); qtempo.setTempoFactor(m_tempoFactor); m_Queue->setTempo(qtempo); m_Client->drainOutput(); if (!m_player->isRunning()) updateTempoLabel(qtempo.getRealBPM()); // Slider tooltip QString tip = QString("%1 %").arg(m_tempoFactor*100.0, 0, 'f', 0); m_ui->sliderTempo->setToolTip(tip); QToolTip::showText(QCursor::pos(), tip, this); } void GUIPlayer::volumeSlider(int value) { QString tip = QString::number(value)+'%'; m_ui->lblVolume->setText(tip); m_ui->volumeSlider->setToolTip(tip); m_player->setVolumeFactor(value); QToolTip::showText(QCursor::pos(), tip, this); } void GUIPlayer::dragEnterEvent( QDragEnterEvent * event ) { if (event->mimeData()->hasUrls()) { event->acceptProposedAction(); } } void GUIPlayer::dropEvent( QDropEvent * event ) { if ( event->mimeData()->hasUrls() ) { QList urls = event->mimeData()->urls(); if (!urls.empty()) { QString fileName = urls.first().toLocalFile(); if ( fileName.endsWith(".mid", Qt::CaseInsensitive) || fileName.endsWith(".midi", Qt::CaseInsensitive) || fileName.endsWith(".kar", Qt::CaseInsensitive) || fileName.endsWith(".rmi", Qt::CaseInsensitive) || fileName.endsWith(".wrk", Qt::CaseInsensitive) ) { stop(); event->accept(); openFile(fileName); } else { QMessageBox::warning(this, QSTR_APPNAME, QString("Dropped file %1 is not supported").arg(fileName)); } } } } void GUIPlayer::readSettings() { QSettings settings; settings.beginGroup("Window"); restoreGeometry(settings.value("Geometry").toByteArray()); restoreState(settings.value("State").toByteArray()); settings.endGroup(); settings.beginGroup("Preferences"); m_lastDirectory = settings.value("LastDirectory").toString(); QString midiConn = settings.value("MIDIConnection").toString(); settings.endGroup(); if (midiConn.length() > 0) subscribe(midiConn); } void GUIPlayer::writeSettings() { QSettings settings; settings.beginGroup("Window"); settings.setValue("Geometry", saveGeometry()); settings.setValue("State", saveState()); settings.endGroup(); settings.beginGroup("Preferences"); settings.setValue("LastDirectory", m_lastDirectory); settings.setValue("MIDIConnection", m_subscription); settings.endGroup(); } void GUIPlayer::closeEvent( QCloseEvent *event ) { stop(); m_player->wait(); writeSettings(); event->accept(); } void GUIPlayer::about() { About aboutDlg(this); aboutDlg.exec(); } /* **************************************** * * SMF (Standard MIDI file) format handling * **************************************** */ void GUIPlayer::smfUpdateLoadProgress() { progressDialogUpdate(m_smf->getFilePos()); } void GUIPlayer::appendSMFEvent(SequencerEvent* ev) { unsigned long tick = m_smf->getCurrentTime(); ev->setSource(m_portId); if (ev->getSequencerType() != SND_SEQ_EVENT_TEMPO) { ev->setSubscribers(); } ev->scheduleTick(m_queueId, tick, false); m_song->append(ev); if (tick > m_tick) m_tick = tick; smfUpdateLoadProgress(); } void GUIPlayer::smfHeaderEvent(int format, int ntrks, int division) { m_song->setHeader(format, ntrks, division); smfUpdateLoadProgress(); } void GUIPlayer::smfNoteOnEvent(int chan, int pitch, int vol) { SequencerEvent* ev = new NoteOnEvent (chan, pitch, vol); appendSMFEvent(ev); } void GUIPlayer::smfNoteOffEvent(int chan, int pitch, int vol) { SequencerEvent* ev = new NoteOffEvent (chan, pitch, vol); appendSMFEvent(ev); } void GUIPlayer::smfKeyPressEvent(int chan, int pitch, int press) { SequencerEvent* ev = new KeyPressEvent (chan, pitch, press); appendSMFEvent(ev); } void GUIPlayer::smfCtlChangeEvent(int chan, int ctl, int value) { SequencerEvent* ev = new ControllerEvent (chan, ctl, value); appendSMFEvent(ev); } void GUIPlayer::smfPitchBendEvent(int chan, int value) { SequencerEvent* ev = new PitchBendEvent (chan, value); appendSMFEvent(ev); } void GUIPlayer::smfProgramEvent(int chan, int patch) { SequencerEvent* ev = new ProgramChangeEvent (chan, patch); appendSMFEvent(ev); } void GUIPlayer::smfChanPressEvent(int chan, int press) { SequencerEvent* ev = new ChanPressEvent (chan, press); appendSMFEvent(ev); } void GUIPlayer::smfSysexEvent(const QByteArray& data) { SequencerEvent* ev = new SysExEvent (data); appendSMFEvent(ev); } void GUIPlayer::smfTempoEvent(int tempo) { if ( m_initialTempo == 0 ) { m_initialTempo = tempo; } SequencerEvent* ev = new TempoEvent (m_queueId, tempo); appendSMFEvent(ev); } void GUIPlayer::smfErrorHandler(const QString& errorStr) { if (m_loadingMessages.length() < 1024) m_loadingMessages.append(QString("%1 at file offset %2
") .arg(errorStr).arg(m_smf->getFilePos())); } void GUIPlayer::smfTrackStarted() { m_currentTrack++; } void GUIPlayer::smfTrackEnded() { if (m_currentTrack == m_smf->getTracks()) { SequencerEvent* ev = new SystemEvent(SND_SEQ_EVENT_ECHO); appendSMFEvent(ev); } } /* ********************************* * * Cakewalk WRK file format handling * ********************************* */ void GUIPlayer::wrkUpdateLoadProgress() { if (m_pd != nullptr) { progressDialogUpdate(m_wrk->getFilePos()); } } void GUIPlayer::appendWRKEvent(unsigned long ticks, SequencerEvent* ev) { ev->setSource(m_portId); if (ev->getSequencerType() != SND_SEQ_EVENT_TEMPO) { ev->setSubscribers(); } ev->scheduleTick(m_queueId, ticks, false); m_song->append(ev); if (ticks > m_tick) m_tick = ticks; wrkUpdateLoadProgress(); } void GUIPlayer::wrkErrorHandler(const QString& errorStr) { if (m_loadingMessages.length() < 1024) { m_loadingMessages.append(QString("%1 at file offset %2
") .arg(errorStr).arg(m_wrk->getFilePos())); } } void GUIPlayer::wrkFileHeader(int /*verh*/, int /*verl*/) { m_song->setHeader(1, 0, 120); wrkUpdateLoadProgress(); // qDebug() << Q_FUNC_INFO; } void GUIPlayer::wrkTimeBase(int timebase) { m_song->setDivision(timebase); wrkUpdateLoadProgress(); // qDebug() << Q_FUNC_INFO << timebase; } void GUIPlayer::wrkStreamEndEvent(long time) { unsigned long ticks = time; if (ticks > m_tick) m_tick = ticks; wrkUpdateLoadProgress(); // qDebug() << Q_FUNC_INFO << time; } void GUIPlayer::wrkTrackHeader( const QString& /*name1*/, const QString& /*name2*/, int trackno, int channel, int pitch, int velocity, int /*port*/, bool /*selected*/, bool /*muted*/, bool /*loop*/ ) { TrackMapRec rec; rec.channel = channel; rec.pitch = pitch; rec.velocity = velocity; m_trackMap[trackno] = rec; wrkUpdateLoadProgress(); // qDebug() << Q_FUNC_INFO << trackno << channel << pitch << velocity; } void GUIPlayer::wrkNoteEvent(int track, long time, int chan, int pitch, int vol, int dur) { TrackMapRec rec = m_trackMap[track]; int channel = (rec.channel > -1) ? rec.channel : chan; int key = qBound(0, pitch + rec.pitch, 127); int velocity = qBound(0, vol + rec.velocity, 127); SequencerEvent* ev = new NoteEvent(channel, key, velocity, dur); appendWRKEvent(time, ev); // qDebug() << Q_FUNC_INFO << channel << key << velocity << dur; } void GUIPlayer::wrkKeyPressEvent(int track, long time, int chan, int pitch, int press) { TrackMapRec rec = m_trackMap[track]; int key = pitch + rec.pitch; int channel = (rec.channel > -1) ? rec.channel : chan; SequencerEvent* ev = new KeyPressEvent(channel, key, press); appendWRKEvent(time, ev); // qDebug() << Q_FUNC_INFO; } void GUIPlayer::wrkCtlChangeEvent(int track, long time, int chan, int ctl, int value) { TrackMapRec rec = m_trackMap[track]; int channel = (rec.channel > -1) ? rec.channel : chan; SequencerEvent* ev = new ControllerEvent(channel, ctl, value); appendWRKEvent(time, ev); // qDebug() << Q_FUNC_INFO; } void GUIPlayer::wrkPitchBendEvent(int track, long time, int chan, int value) { TrackMapRec rec = m_trackMap[track]; int channel = (rec.channel > -1) ? rec.channel : chan; SequencerEvent* ev = new PitchBendEvent(channel, value); appendWRKEvent(time, ev); // qDebug() << Q_FUNC_INFO; } void GUIPlayer::wrkProgramEvent(int track, long time, int chan, int patch) { TrackMapRec rec = m_trackMap[track]; int channel = (rec.channel > -1) ? rec.channel : chan; SequencerEvent* ev = new ProgramChangeEvent(channel, patch); appendWRKEvent(time, ev); // qDebug() << Q_FUNC_INFO; } void GUIPlayer::wrkChanPressEvent(int track, long time, int chan, int press) { TrackMapRec rec = m_trackMap[track]; int channel = (rec.channel > -1) ? rec.channel : chan; SequencerEvent* ev = new ChanPressEvent(channel, press); appendWRKEvent(time, ev); // qDebug() << Q_FUNC_INFO; } void GUIPlayer::wrkSysexEvent(int track, long time, int bank) { Q_UNUSED(track) qDebug() << Q_FUNC_INFO; if (m_savedSysexEvents.contains(bank)) { SysExEvent* ev = m_savedSysexEvents[bank].clone(); appendWRKEvent(time, ev); wrkUpdateLoadProgress(); } } void GUIPlayer::wrkSysexEventBank(int bank, const QString& /*name*/, bool autosend, int /*port*/, const QByteArray& data) { //qDebug() << Q_FUNC_INFO; SysExEvent* ev = new SysExEvent(data); if (autosend) { appendWRKEvent(0, ev); } else { m_savedSysexEvents[bank] = *ev; delete ev; } wrkUpdateLoadProgress(); } void GUIPlayer::wrkTempoEvent(long time, int tempo) { double bpm = tempo / 100.0; if ( m_initialTempo < 0 ) m_initialTempo = qRound( bpm ); SequencerEvent* ev = new TempoEvent(m_queueId, qRound ( 6e7 / bpm ) ); appendWRKEvent(time, ev); // qDebug() << Q_FUNC_INFO; } void GUIPlayer::wrkTrackPatch(int track, int patch) { TrackMapRec rec = m_trackMap[track]; int channel = (rec.channel > -1) ? rec.channel : 0; wrkProgramEvent(track, 0, channel, patch); // qDebug() << Q_FUNC_INFO; } void GUIPlayer::wrkNewTrackHeader( const QString& /*name*/, int trackno, int channel, int pitch, int velocity, int /*port*/, bool /*selected*/, bool /*muted*/, bool /*loop*/ ) { TrackMapRec rec; rec.channel = channel; rec.pitch = pitch; rec.velocity = velocity; m_trackMap[trackno] = rec; wrkUpdateLoadProgress(); // qDebug() << Q_FUNC_INFO << trackno << channel << pitch << velocity; } void GUIPlayer::wrkTrackVol(int track, int vol) { int lsb, msb; TrackMapRec rec = m_trackMap[track]; int channel = (rec.channel > -1) ? rec.channel : 0; if (vol < 128) wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_MSB_MAIN_VOLUME, vol); else { lsb = vol % 0x80; msb = vol / 0x80; wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_LSB_MAIN_VOLUME, lsb); wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_MSB_MAIN_VOLUME, msb); } // qDebug() << Q_FUNC_INFO; } void GUIPlayer::wrkTrackBank(int track, int bank) { // assume GM/GS bank method int lsb, msb; TrackMapRec rec = m_trackMap[track]; int channel = (rec.channel > -1) ? rec.channel : 0; lsb = bank % 0x80; msb = bank / 0x80; wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_MSB_BANK, msb); wrkCtlChangeEvent(track, 0, channel, MIDI_CTL_LSB_BANK, lsb); // qDebug() << Q_FUNC_INFO; } void GUIPlayer::wrkEndOfFile() { if (m_initialTempo < 0) m_initialTempo = 120; SequencerEvent* ev = new SystemEvent(SND_SEQ_EVENT_ECHO); appendWRKEvent(m_tick, ev); // qDebug() << Q_FUNC_INFO; } DISABLE_WARNING_POP drumstick-2.9.0/utils/guiplayer/guiplayer.qrc0000644000175000017500000000070214541630232020403 0ustar pedropedro ../../icons/drumstick_32.png resources/qtlogo.png resources/about.png resources/open.png resources/pause.png resources/play.png resources/quit.png resources/setup.png resources/stop.png resources/vol.png drumstick-2.9.0/utils/guiplayer/guiplayer.ui0000644000175000017500000004631614541630232020246 0ustar pedropedro GUIPlayerClass 0 0 438 302 Drumstick ALSA MIDI Player :/drumstick.png:/drumstick.png true Drumstick MIDI Player 0 0 255 255 255 255 255 255 0 0 0 0 0 0 255 255 255 255 255 255 0 0 0 0 0 0 146 145 144 165 164 164 0 0 0 0 0 0 Playback time and current levels true QFrame::NoFrame QFrame::Plain 4 0 0 210 62 210 62 34 00:00:00 Tempo: 0 0 100 0 Volume: Pitch: 50 16777215 Pitch Control Pitch transpose between -12 and +12 semitones -12 12 Reset Volume Reset Volume to 100% :/resources/vol.png:/resources/vol.png 0 0 Volume Slider 200 100 false Qt::Vertical QSlider::TicksAbove 0 0 Playback progress 0 Qt::AlignCenter true QProgressBar::TopToBottom QFormLayout::ExpandingFieldsGrow File Name: 0 0 Currently loaded MIDI file name true Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse 0 0 Tempo Slider 0 200 1 10 100 Qt::Horizontal QSlider::TicksAbove 5 0 0 Reset Tempo Reset Tempo to 100% tempo=100% 0 0 438 23 File Help Settings Tool Bar Qt::ToolButtonTextUnderIcon BottomToolBarArea false :/resources/open.png:/resources/open.png Open Open a MIDI file :/resources/quit.png:/resources/quit.png Quit Quit the application :/resources/play.png:/resources/play.png Play Start playing the current MIDI file false true true :/resources/pause.png:/resources/pause.png Pause Pause the playback false :/resources/stop.png:/resources/stop.png Stop Stop the playback false :/resources/about.png:/resources/about.png About Show the about box :/resources/qtlogo.png:/resources/qtlogo.png About Qt Show the about Qt dialog box :/resources/setup.png:/resources/setup.png MIDI Setup Select a connection for the MIDI output port true true Show Tool Bar Show Tool Bar Show or hide the tool bar true true Show Status Bar Show or hide the status bar spinPitch sliderTempo btnTempo volumeSlider actionShowStatusbar toggled(bool) myStatusBar setVisible(bool) -1 -1 216 296 actionShowToolbar toggled(bool) toolBar setVisible(bool) -1 -1 216 51 drumstick-2.9.0/utils/guiplayer/iconutils.cpp0000644000175000017500000000255714541630232020422 0ustar pedropedro/* SMF GUI Player test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "iconutils.h" #include #include namespace IconUtils { void PaintPixmap(QPixmap &pixmap, const QColor& color) { QPainter painter(&pixmap); painter.setCompositionMode(QPainter::CompositionMode_SourceIn); painter.fillRect(pixmap.rect(), color); } QPixmap GetPixmap(QWidget* widget, const QString& fileName) { QPixmap pixmap(fileName); QColor color = widget->palette().color(QPalette::Active, QPalette::WindowText); PaintPixmap(pixmap, color); return pixmap; } } // namespace IconUtils drumstick-2.9.0/utils/guiplayer/iconutils.h0000644000175000017500000000207314541630232020060 0ustar pedropedro/* SMF GUI Player test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ICONUTILS_H #define ICONUTILS_H #include #include #include #include namespace IconUtils { void PaintPixmap(QPixmap &pixmap, const QColor& color); QPixmap GetPixmap(QWidget* widget, const QString& fileName); } #endif // ICONUTILS_H drumstick-2.9.0/utils/guiplayer/player.cpp0000644000175000017500000001160714541630232017701 0ustar pedropedro/* SMF GUI Player test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "player.h" #include "song.h" #include #include #include using namespace drumstick::ALSA; Player::Player(MidiClient *seq, int portId) : SequencerOutputThread(seq, portId), m_song(nullptr), m_songIterator(nullptr), m_lastEvent(nullptr), m_songPosition(0), m_echoResolution(0), m_pitchShift(0), m_volumeFactor(100) { for (int chan = 0; chan < MIDI_CHANNELS; ++chan) m_volume[chan] = 100; } Player::~Player() { if (isRunning()) { stop(); } delete m_songIterator; delete m_lastEvent; } void Player::setSong(Song* s) { m_song = s; if (m_song != nullptr) { delete m_songIterator; m_songIterator = new SongIterator(*m_song); m_echoResolution = m_song->getDivision() / 12; m_songPosition = 0; } } void Player::resetPosition() { if ((m_song != nullptr) && (m_songIterator != nullptr)) { m_songIterator->toFront(); m_songPosition = 0; } } void Player::setPosition(unsigned int pos) { m_songPosition = pos; m_songIterator->toFront(); while (m_songIterator->hasNext() && (m_songIterator->next()->getTick() < pos)) { }; if (m_songIterator->hasPrevious()) m_songIterator->previous(); } bool Player::hasNext() { auto res = m_songIterator->hasNext(); return res; } SequencerEvent* Player::nextEvent() { delete m_lastEvent; m_lastEvent = m_songIterator->next()->clone(); switch (m_lastEvent->getSequencerType()) { case SND_SEQ_EVENT_NOTE: case SND_SEQ_EVENT_NOTEON: case SND_SEQ_EVENT_NOTEOFF: case SND_SEQ_EVENT_KEYPRESS: { KeyEvent* kev = static_cast(m_lastEvent); if (kev->getChannel() != MIDI_GM_DRUM_CHANNEL) kev->setKey(kev->getKey() + m_pitchShift); } break; case SND_SEQ_EVENT_CONTROLLER: { ControllerEvent *cev = static_cast(m_lastEvent); if (cev->getParam() == MIDI_CTL_MSB_MAIN_VOLUME) { int chan = cev->getChannel(); int value = cev->getValue(); m_volume[chan] = value; value = floor(value * m_volumeFactor / 100.0); if (value < 0) value = 0; if (value > 127) value = 127; cev->setValue(value); } } break; } return m_lastEvent; } unsigned int Player::getInitialPosition() { return m_songPosition; } unsigned int Player::getEchoResolution() { return m_echoResolution; } unsigned int Player::getPitchShift() { return m_pitchShift; } unsigned int Player::getVolumeFactor() { return m_volumeFactor; } void Player::setPitchShift(unsigned int pitch) { bool playing = isRunning(); if (playing) { stop(); unsigned int pos = m_Queue->getStatus().getTickTime(); m_Queue->clear(); allNotesOff(); setPosition(pos); } m_pitchShift = pitch; if (playing) start(); } void Player::setVolumeFactor(unsigned int vol) { m_volumeFactor = vol; for(int chan = 0; chan < MIDI_CHANNELS; ++chan) { int value = m_volume[chan]; value = floor(value * m_volumeFactor / 100.0); if (value < 0) value = 0; if (value > 127) value = 127; sendController(chan, MIDI_CTL_MSB_MAIN_VOLUME, value); } } void Player::sendController(int chan, int control, int value) { ControllerEvent ev(chan, control, value); ev.setSource(m_PortId); ev.setSubscribers(); ev.setDirect(); sendSongEvent(&ev); } void Player::allNotesOff() { for(int chan = 0; chan < MIDI_CHANNELS; ++chan) { sendController(chan, MIDI_CTL_ALL_NOTES_OFF, 0); sendController(chan, MIDI_CTL_ALL_SOUNDS_OFF, 0); } } void Player::sendVolumeEvents() { for(int chan = 0; chan < MIDI_CHANNELS; ++chan) { int value = m_volume[chan] = 100; value = floor(value * m_volumeFactor / 100.0); if (value < 0) value = 0; if (value > 127) value = 127; sendController(chan, MIDI_CTL_MSB_MAIN_VOLUME, value); } } drumstick-2.9.0/utils/guiplayer/player.h0000644000175000017500000000364314541630232017347 0ustar pedropedro/* SMF GUI Player test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef INCLUDED_PLAYER_H #define INCLUDED_PLAYER_H #include #include "song.h" class Player : public drumstick::ALSA::SequencerOutputThread { Q_OBJECT public: Player(drumstick::ALSA::MidiClient *seq, int portId); virtual ~Player(); virtual bool hasNext() override; virtual drumstick::ALSA::SequencerEvent* nextEvent() override; virtual unsigned int getInitialPosition() override; virtual unsigned int getEchoResolution() override; unsigned int getPitchShift(); unsigned int getVolumeFactor(); void setSong(Song* s); void resetPosition(); void setPosition(unsigned int pos); void setPitchShift(unsigned int pitch); void setVolumeFactor(unsigned int vol); void sendController(int chan, int control, int value); void allNotesOff(); void sendVolumeEvents(); private: Song* m_song; SongIterator* m_songIterator; drumstick::ALSA::SequencerEvent* m_lastEvent; unsigned int m_songPosition; unsigned int m_echoResolution; unsigned int m_pitchShift; unsigned int m_volumeFactor; int m_volume[MIDI_CHANNELS]; }; #endif /*INCLUDED_PLAYER_H*/ drumstick-2.9.0/utils/guiplayer/playerabout.cpp0000644000175000017500000000252314541630232020731 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "playerabout.h" About::About(QWidget *parent) : QDialog(parent) { ui.setupUi(this); QString aboutText = ui.AboutTextView->toHtml(); QString strver(QT_STRINGIFY(VERSION)); #ifdef REVISION strver.append("
"); strver.append(tr("Revision")); strver.append(" "); strver.append(QT_STRINGIFY(REVISION)); #endif aboutText.replace("%VERSION%", strver); aboutText.replace("%QT_VERSION%", qVersion()); ui.AboutTextView->setHtml(aboutText); connect(ui.aboutQt, &QPushButton::clicked, qApp, QApplication::aboutQt); } drumstick-2.9.0/utils/guiplayer/playerabout.h0000644000175000017500000000200214541630232020366 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ABOUT_H #define ABOUT_H #include #include "ui_playerabout.h" class About : public QDialog { Q_OBJECT public: explicit About(QWidget *parent = nullptr); private: Ui::AboutClass ui; }; #endif // ABOUT_H drumstick-2.9.0/utils/guiplayer/playerabout.ui0000644000175000017500000001506214541630232020566 0ustar pedropedro AboutClass 0 0 468 337 About :/drumstick.png:/drumstick.png false true <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick ALSA MIDI Player %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Using Qt version: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> true About Qt :/resources/qtlogo.png:/resources/qtlogo.png QDialogButtonBox::Close buttonBox rejected() AboutClass close() 306 315 325 335 drumstick-2.9.0/utils/guiplayer/playermain.cpp0000644000175000017500000001123714541630232020545 0ustar pedropedro/* SMF GUI Player test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "guiplayer.h" const char* PGM_DESCRIPTION = QT_TRANSLATE_NOOP("main", "ALSA Sequencer based MIDI file player"); const char* errorstr = QT_TRANSLATE_NOOP("main", "Fatal error from the ALSA sequencer. " "This usually happens when the kernel doesn't have ALSA support, " "or the device node (/dev/snd/seq) doesn't exists, " "or the kernel module (snd_seq) is not loaded. " "Please check your ALSA/MIDI configuration."); int main(int argc, char *argv[]) { QApplication app(argc, argv); QCoreApplication::setOrganizationName(GUIPlayer::QSTR_DOMAIN); QCoreApplication::setOrganizationDomain(GUIPlayer::QSTR_DOMAIN); QCoreApplication::setApplicationName(GUIPlayer::QSTR_APPNAME); QCoreApplication::setApplicationVersion(QStringLiteral(QT_STRINGIFY(VERSION))); QGuiApplication::setDesktopFileName("net.sourceforge.drumstick-guiplayer"); QLocale locale; QTranslator qtTranslator; if ((locale.language() != QLocale::C) && (locale.language() != QLocale::English)) { if (qtTranslator.load(locale, "qt", "_", #if QT_VERSION < QT_VERSION_CHECK(6,0,0) QLibraryInfo::location(QLibraryInfo::TranslationsPath) #else QLibraryInfo::path(QLibraryInfo::TranslationsPath) #endif )) { QCoreApplication::installTranslator(&qtTranslator); } else { qWarning() << "Unable to load Qt translator:" << locale.name(); } } #if defined(Q_OS_WIN32) QString dataDir = QApplication::applicationDirPath() + "/"; #elif defined(Q_OS_MAC) QString dataDir = QApplication::applicationDirPath() + "/../Resources/"; #else QString dataDir = QApplication::applicationDirPath() + "/../share/drumstick/"; #endif QTranslator appTranslator; if ((locale.language() != QLocale::C) && (locale.language() != QLocale::English)) { if (appTranslator.load(locale, "drumstick-guiplayer", "_", dataDir)) { QCoreApplication::installTranslator(&appTranslator); } else { qWarning() << "Unable to load app translator:" << locale.name(); } } QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::translate("main", PGM_DESCRIPTION)); auto helpOption = parser.addHelpOption(); auto versionOption = parser.addVersionOption(); QCommandLineOption portOption({"p", "port"}, QCoreApplication::translate("main", "MIDI Out Connection."), "client:port"); parser.addOption(portOption); parser.addPositionalArgument("file", QCoreApplication::translate("main", "Input SMF/KAR/RMI/WRK file name."), "file"); parser.process(app); QStringList fileNames, positionalArgs = parser.positionalArguments(); foreach(const QString& a, positionalArgs) { QFileInfo f(a); if (f.exists()) fileNames += f.canonicalFilePath(); else qWarning() << QCoreApplication::translate("main", "File not found: ") << a; } try { GUIPlayer w; if(parser.isSet(portOption)) { QString port = parser.value(portOption); w.subscribe(port); } if (!fileNames.isEmpty()) { w.openFile(fileNames.first()); } w.show(); return app.exec(); } catch (const drumstick::ALSA::SequencerError& ex) { QMessageBox::critical(nullptr, QCoreApplication::translate("main", "Error"), QCoreApplication::translate("main", errorstr) + "\n" + QCoreApplication::translate("main", "Returned error was: ") + ex.qstrError() ); } catch (...) { qWarning() << QCoreApplication::translate("main", errorstr); } return EXIT_FAILURE; } drumstick-2.9.0/utils/guiplayer/resources/0000755000175000017500000000000014541630232017706 5ustar pedropedrodrumstick-2.9.0/utils/guiplayer/resources/about.png0000644000175000017500000000152014541630232021524 0ustar pedropedroPNG  IHDRasBIT|d pHYsjjtEXtSoftwarewww.inkscape.org<IDAT8UOhWǿ3vdCtu졩HĶ-z(5w%E*xzb HE2qǥϻ qxsG8;.q[6OKq0b1SySGPMRzS- @U[|wlͽeĎ&?``wh[Z[l 4(p> ðbG1*XNF>\į&„ٗ 4B a^T $!0#;^\H8UއyŽBt8Rm ^Z&u!m lAyh44'_j5XVbs -ùT3*!wzR_Nn,7 vvrpf dۏ^|mZp6cqSL`"PDJŹ_w[oߧB㑍|1E= 6޼y\r{Hatddd6slc.e T 4]5~bf=t`xLIENDB`drumstick-2.9.0/utils/guiplayer/resources/drumstick.png0000644000175000017500000000103614541630232022421 0ustar pedropedroPNG  IHDRasBIT|d pHYsNtEXtSoftwarewww.inkscape.org<IDAT8Kﳭ{׻" H/bD!(b)LA֭i%:B)"QT(ba(fcctק"?DU$  "rǝuΝ>bqJ|MQUD|b 4,쨅mY彅 lRgwQZެVZ ˛t9>$#@:뗸8pNPcG#Hm#esG':AY#S]+nyKEWnTӞhR7VJ d2[᚜h G3'pA\>MWȆ ;ܕ?("zIQ-֙a3\&QoASZD$D+ȑ( x+~,TRHIENDB`drumstick-2.9.0/utils/guiplayer/resources/open.png0000644000175000017500000000040514541630232021354 0ustar pedropedroPNG  IHDRaIDATxڥS 0 sNlB l#AM:H!GR|e *}TՙfZavc4H)yK̢59#"rUO{ђv3;avM"O,0B"ԃeCm.1맃;|]хGl/C6`yEuEqpx# PL1GO*"ve+z_5:ǍIENDB`drumstick-2.9.0/utils/guiplayer/resources/pause.png0000644000175000017500000000024314541630232021530 0ustar pedropedroPNG  IHDRabKGD pHYs  tIME '0IDAT8c?%B0 022ĆQ8^bJ5IENDB`drumstick-2.9.0/utils/guiplayer/resources/play.png0000644000175000017500000000023714541630232021363 0ustar pedropedroPNG  IHDRagAMA a pHYs  ~AIDATxc`? (5DBc^C5!@ )1fb jNųIENDB`drumstick-2.9.0/utils/guiplayer/resources/qtlogo.png0000644000175000017500000000126114541630232021721 0ustar pedropedroPNG  IHDRabKGD pHYs@tIME %$#+>IDAT8}MHTaO3wqь! +haVAP-2lѢE?rYZHRJ!J_* ͟bi̽Zӌ/p2]GEN0)G!3#'҆26ȑX7SHJ,KT0" Jگ/ ;Waϑ;@*{IC" FOkd3q>ڛp.[:Mu1F¡IENDB`drumstick-2.9.0/utils/guiplayer/resources/setup.png0000644000175000017500000000073614541630232021562 0ustar pedropedroPNG  IHDRasBIT|d pHYsSSbjtEXtSoftwarewww.inkscape.org<[IDAT8ӿjTQl\żK,k!,XLekge} ډ`U@7v.u\7j/@/<ՂP`!s*ȟ3wӶ'VZK8ض̠]2uYZKz7 ǏTfhhh@3f!Mf_4@dӞĪ'oz.=e|OYYRJ(X Mӈ$4ҪSGwdv_] ukb&ibYըJ$/ϣq?8V )xTA@׉c㠪*m:a-#)4@^nQPTf@A @UU|>IΎP )9F1MEQH&\Rs>p hYRRipoޡ>F:l'٪޻ZjrsH޹atxEq1ҔW*9xs34MCu222p32<'§n This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "song.h" #include using namespace drumstick::ALSA; static inline bool eventLessThan(const SequencerEvent* s1, const SequencerEvent* s2) { return s1->getTick() < s2->getTick(); } Song::~Song() { clear(); } void Song::sort() { std::sort(begin(), end(), eventLessThan); } void Song::clear() { while (!isEmpty()) delete takeFirst(); m_fileName.clear(); m_format = 0; m_ntrks = 0; m_division = 0; } void Song::setHeader(int format, int ntrks, int division) { m_format = format; m_ntrks = ntrks; m_division = division; } void Song::setDivision(int division) { m_division = division; } void Song::setFileName(const QString& fileName) { m_fileName = fileName; } drumstick-2.9.0/utils/guiplayer/song.h0000644000175000017500000000331014541630232017010 0ustar pedropedro/* SMF GUI Player test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef INCLUDED_SONG_H #define INCLUDED_SONG_H #include namespace drumstick { namespace ALSA { class SequencerEvent; }} class Song : public QList { public: Song() : QList(), m_format(0), m_ntrks(0), m_division(0) { } virtual ~Song(); void clear(); void sort(); void setHeader(int format, int ntrks, int division); void setDivision(int division); void setFileName(const QString& fileName); int getFormat() const { return m_format; } int getTracks() const { return m_ntrks; } int getDivision() const { return m_division; } QString getFileName() const { return m_fileName; } private: int m_format; int m_ntrks; int m_division; QString m_fileName; }; typedef QListIterator SongIterator; #endif /*INCLUDED_SONG_H*/ drumstick-2.9.0/utils/guiplayer/guiplayer.pro0000644000175000017500000000142414541630232020420 0ustar pedropedroTEMPLATE = app TARGET = drumstick-guiplayer QT += gui widgets #dbus equals(QT_MAJOR_VERSION, 6) { QT += core5compat } CONFIG += qt c++11 thread exceptions CONFIG += lrelease static { CONFIG += link_prl } DESTDIR = ../../build/bin LRELEASE_DIR=. INCLUDEPATH += . ../../library/include LIBS = -L../../build/lib -ldrumstick-file -ldrumstick-alsa -lasound include (../../global.pri) # Input HEADERS += player.h guiplayer.h song.h playerabout.h iconutils.h FORMS += guiplayer.ui playerabout.ui SOURCES += playermain.cpp \ player.cpp \ guiplayer.cpp \ song.cpp \ iconutils.cpp \ playerabout.cpp RESOURCES += guiplayer.qrc TRANSLATIONS += \ drumstick-guiplayer_cs.ts \ drumstick-guiplayer_en.ts \ drumstick-guiplayer_es.ts \ drumstick-guiplayer_ru.ts drumstick-2.9.0/utils/guiplayer/drumstick-guiplayer.metainfo.xml0000644000175000017500000000251214541630232024223 0ustar pedropedro net.sourceforge.drumstick-guiplayer drumstick-guiplayer Simple MIDI file player based on ALSA Sequencer FSFAP GPL-3.0+

Simple MIDI file player for .MID (Standard MIDI Files), .RMI (RIFF RMID) and .WRK (Cakewalk) files based on the ALSA Sequencer.

This program is an example of the Drumstick libraries. For a fully featured MIDI file player see: dmidiplayer.sourceforge.io

net.sourceforge.drumstick-guiplayer.desktop https://drumstick.sourceforge.io/images/drumstick-guiplayer.png
drumstick-2.9.0/utils/guiplayer/CMakeLists.txt0000644000175000017500000000635614541630232020446 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(CMAKE_INCLUDE_CURRENT_DIR TRUE) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Gui Widgets LinguistTools REQUIRED) set(guiplayer_forms_SRCS guiplayer.ui playerabout.ui ) set(guiplayer_SRCS guiplayer.cpp guiplayer.h iconutils.cpp iconutils.h player.cpp player.h playerabout.cpp playerabout.h playermain.cpp song.cpp song.h ) set(guiplayer_qtobject_SRCS player.h playerabout.h guiplayer.h ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_ui(guiplayer_ui_SRCS ${guiplayer_forms_SRCS}) qt5_wrap_cpp(guiplayer_moc_SRCS ${guiplayer_qtobject_SRCS}) qt5_add_resources(guiplayer_resources guiplayer.qrc) else() qt_wrap_ui(guiplayer_ui_SRCS ${guiplayer_forms_SRCS}) qt_wrap_cpp(guiplayer_moc_SRCS ${guiplayer_qtobject_SRCS}) qt_add_resources(guiplayer_resources guiplayer.qrc) endif() add_executable(drumstick-guiplayer ${guiplayer_resources} ${guiplayer_ui_SRCS} ${guiplayer_moc_SRCS} ${guiplayer_SRCS} ) target_link_libraries(drumstick-guiplayer PRIVATE Drumstick::File Drumstick::ALSA Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets ) if(QT_VERSION VERSION_GREATER_EQUAL 6.0.0) target_link_libraries(drumstick-guiplayer PRIVATE Qt6::Core5Compat) endif() set(TS_FILES drumstick-guiplayer_cs.ts drumstick-guiplayer_es.ts drumstick-guiplayer_it.ts drumstick-guiplayer_ru.ts ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_add_translation(QM_FILES ${TS_FILES}) else() qt_add_translation(QM_FILES ${TS_FILES}) endif() add_custom_target( update-guiplayer-translations COMMAND Qt${QT_VERSION_MAJOR}::lupdate ${CMAKE_CURRENT_SOURCE_DIR} -ts ${TS_FILES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Updating guiplayer translations" ) add_custom_target( drumstick-guiplayer-translations ALL DEPENDS ${QM_FILES} ) if (UNIX AND NOT APPLE) install( FILES ${QM_FILES} DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/drumstick" ) endif () install(TARGETS drumstick-guiplayer RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(FILES drumstick-guiplayer.desktop DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications" RENAME net.sourceforge.drumstick-guiplayer.desktop) install(FILES drumstick-guiplayer.metainfo.xml DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo" RENAME net.sourceforge.drumstick-guiplayer.metainfo.xml) drumstick-2.9.0/utils/guiplayer/guiplayer.h0000644000175000017500000001346114541630232020053 0ustar pedropedro/* SMF GUI Player test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef INCLUDED_GUIPLAYER_H #define INCLUDED_GUIPLAYER_H #include #include #include #include #include #include #include namespace drumstick { namespace ALSA { class MidiClient; class MidiPort; class MidiQueue; class SequencerEvent; class SysExEvent; } namespace File { class QSmf; class QWrk; class Rmidi; } } namespace Ui { class GUIPlayerClass; } class Player; class About; class Song; class GUIPlayer : public QMainWindow { Q_OBJECT public: enum PlayerState { InvalidState, EmptyState, PlayingState, PausedState, StoppedState }; Q_ENUM(PlayerState) GUIPlayer(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::Window); ~GUIPlayer(); void appendSMFEvent(drumstick::ALSA::SequencerEvent* ev); void appendWRKEvent(unsigned long ticks, drumstick::ALSA::SequencerEvent* ev); void subscribe(const QString& portName); void updateTimeLabel(int mins, int secs, int cnts); void updateTempoLabel(float ftempo); void dragEnterEvent(QDragEnterEvent* event) override; void dropEvent(QDropEvent* event) override; void closeEvent(QCloseEvent* event) override; void openFile(const QString& fileName); void readSettings(); void writeSettings(); void updateState(PlayerState newState); void progressDialogInit(const QString& type, int max); void progressDialogUpdate(int pos); void progressDialogClose(); static const QString QSTR_DOMAIN; static const QString QSTR_APPNAME; public Q_SLOTS: void about(); void play(); void pause(); void stop(); void open(); void setup(); void tempoReset(); void volumeReset(); void tempoSlider(int value); void volumeSlider(int value); void pitchShift(int value); void songFinished(); void playerStopped(); void sequencerEvent(drumstick::ALSA::SequencerEvent* ev); /* RMI slots */ void dataHandler(const QString &dataType, const QByteArray &data); /* SMF slots */ void smfHeaderEvent(int format, int ntrks, int division); void smfNoteOnEvent(int chan, int pitch, int vol); void smfNoteOffEvent(int chan, int pitch, int vol); void smfKeyPressEvent(int chan, int pitch, int press); void smfCtlChangeEvent(int chan, int ctl, int value); void smfPitchBendEvent(int chan, int value); void smfProgramEvent(int chan, int patch); void smfChanPressEvent(int chan, int press); void smfSysexEvent(const QByteArray& data); void smfTempoEvent(int tempo); void smfErrorHandler(const QString& errorStr); void smfTrackStarted(); void smfTrackEnded(); void smfUpdateLoadProgress(); /* WRK slots */ void wrkUpdateLoadProgress(); void wrkErrorHandler(const QString& errorStr); void wrkFileHeader(int verh, int verl); void wrkEndOfFile(); void wrkStreamEndEvent(long time); void wrkTrackHeader(const QString& name1, const QString& name2, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop); void wrkTimeBase(int timebase); void wrkNoteEvent(int track, long time, int chan, int pitch, int vol, int dur); void wrkKeyPressEvent(int track, long time, int chan, int pitch, int press); void wrkCtlChangeEvent(int track, long time, int chan, int ctl, int value); void wrkPitchBendEvent(int track, long time, int chan, int value); void wrkProgramEvent(int track, long time, int chan, int patch); void wrkChanPressEvent(int track, long time, int chan, int press); void wrkSysexEvent(int track, long time, int bank); void wrkSysexEventBank(int bank, const QString& name, bool autosend, int port, const QByteArray& data); void wrkTempoEvent(long time, int tempo); void wrkTrackPatch(int track, int patch); void wrkNewTrackHeader(const QString& name, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop); void wrkTrackVol(int track, int vol); void wrkTrackBank(int track, int bank); private: int m_portId; int m_queueId; int m_initialTempo; int m_currentTrack; float m_tempoFactor; unsigned long m_tick; PlayerState m_state; drumstick::File::QSmf* m_smf; drumstick::File::QWrk* m_wrk; drumstick::File::Rmidi* m_rmi; drumstick::ALSA::MidiClient* m_Client; drumstick::ALSA::MidiPort* m_Port; drumstick::ALSA::MidiQueue* m_Queue; Player* m_player; Ui::GUIPlayerClass* m_ui; QPointer m_pd; Song* m_song; QString m_subscription; QString m_lastDirectory; QString m_loadingMessages; QHash m_savedSysexEvents; struct TrackMapRec { int channel; int pitch; int velocity; }; QHash m_trackMap; }; #endif // INCLUDED_GUIPLAYER_H drumstick-2.9.0/utils/metronome/0000755000175000017500000000000014541630232015700 5ustar pedropedrodrumstick-2.9.0/utils/metronome/metronome.cpp0000644000175000017500000002106614541630232020416 0ustar pedropedro/* Standard MIDI simple metronome Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "metronome.h" #include #include #include #include #include #include #include #include #include #include #include #include #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) #define right Qt::right #define left Qt::left #define endl Qt::endl #endif QTextStream cout(stdout, QIODevice::WriteOnly); QTextStream cerr(stderr, QIODevice::WriteOnly); using namespace drumstick::ALSA; /* *************** * * Metronome class * * *************** */ Metronome::Metronome(QObject *parent) : QObject(parent), m_weak_note(METRONOME_WEAK_NOTE), m_strong_note(METRONOME_STRONG_NOTE), m_weak_velocity(METRONOME_VELOCITY), m_strong_velocity(METRONOME_VELOCITY), m_program(METRONOME_PROGRAM), m_channel(METRONOME_CHANNEL), m_volume(METRONOME_VOLUME), m_pan(METRONOME_PAN), m_resolution(METRONOME_RESOLUTION), m_bpm(TEMPO_DEFAULT), m_ts_num(RHYTHM_TS_NUM), m_ts_div(RHYTHM_TS_DEN), m_noteDuration(NOTE_DURATION), m_portId(-1), m_queueId(-1), m_clientId(-1), m_Stopped(true) { QString name{QStringLiteral("Metronome")}; m_Client = new MidiClient(this); m_Client->open(); m_Client->setClientName(name); m_Client->setHandler(this); m_Port = new MidiPort(this); m_Port->attach( m_Client ); m_Port->setPortName(name); m_Port->setCapability( SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ | SND_SEQ_PORT_CAP_WRITE ); m_Port->setPortType( SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION ); m_Queue = m_Client->createQueue(name); m_clientId = m_Client->getClientId(); m_queueId = m_Queue->getId(); m_portId = m_Port->getPortId(); m_Port->setTimestamping(true); m_Port->setTimestampQueue(m_queueId); // Get and apply the best available timer TimerId best = Timer::bestGlobalTimerId(); QueueTimer qtimer; qtimer.setId(best); m_Queue->setTimer(qtimer); // Start sequencer input m_Client->setRealTimeInput(false); m_Client->startSequencerInput(); } Metronome::~Metronome() { m_Port->detach(); m_Client->close(); } void Metronome::handleSequencerEvent( SequencerEvent *ev ) { if (ev->getSequencerType() == SND_SEQ_EVENT_USR0) metronome_pattern(static_cast(ev->getTick()) + m_patternDuration); delete ev; } void Metronome::metronome_event_output(SequencerEvent* ev) { ev->setSource(static_cast(m_portId)); ev->setSubscribers(); ev->setDirect(); m_Client->outputDirect(ev); } void Metronome::sendControlChange(int cc, int value) { ControllerEvent ev(m_channel, cc, value); metronome_event_output(&ev); } void Metronome::sendInitialControls() { metronome_set_program(); metronome_set_controls(); metronome_set_tempo(); } void Metronome::metronome_set_program() { ProgramChangeEvent ev(m_channel, m_program); metronome_event_output(&ev); } void Metronome::metronome_schedule_event(SequencerEvent* ev, int tick, bool lb) { ev->setSource(static_cast(m_portId)); if (lb) // loop back ev->setDestination(static_cast(m_clientId), static_cast(m_portId)); else ev->setSubscribers(); ev->scheduleTick(m_queueId, tick, false); m_Client->outputDirect(ev); } void Metronome::metronome_note(int note, int tick) { NoteEvent ev(m_channel, note, METRONOME_VELOCITY, m_noteDuration); metronome_schedule_event(&ev, tick, false); } void Metronome::metronome_echo(int tick) { SystemEvent ev(SND_SEQ_EVENT_USR0); metronome_schedule_event(&ev, tick, true); } void Metronome::metronome_pattern(int tick) { int j, t, duration; t = tick; duration = m_resolution * 4 / m_ts_div; for (j = 0; j < m_ts_num; j++) { metronome_note(j ? m_weak_note : m_strong_note, t); t += duration; } metronome_echo(t); } void Metronome::metronome_set_tempo() { QueueTempo t = m_Queue->getTempo(); t.setPPQ(m_resolution); t.setNominalBPM(m_bpm); m_Queue->setTempo(t); m_Client->drainOutput(); } void Metronome::metronome_set_controls() { sendControlChange(MIDI_CTL_MSB_MAIN_VOLUME, m_volume); sendControlChange(MIDI_CTL_MSB_PAN, m_pan); } void Metronome::subscribe(const QString& portName) { m_Port->subscribeTo(portName); } bool Metronome::stopped() { QReadLocker locker(&m_mutex); return m_Stopped; } void Metronome::stop() { QWriteLocker locker(&m_mutex); m_Stopped = true; m_Client->dropOutput(); } void Metronome::shutupSound() { sendControlChange( MIDI_CTL_ALL_NOTES_OFF, 0 ); sendControlChange( MIDI_CTL_ALL_SOUNDS_OFF, 0 ); } void Metronome::play(QString tempo) { bool ok; m_Stopped = false; m_patternDuration = m_resolution * 4 / m_ts_div * m_ts_num; m_bpm = tempo.toInt(&ok); if (!ok) m_bpm = TEMPO_DEFAULT; cout << "Metronome playing. " << m_bpm << " bpm" << endl; cout << "Press Ctrl+C to exit" << endl; try { sendInitialControls(); m_Queue->start(); metronome_pattern(0); metronome_pattern(m_patternDuration); while (!stopped()) sleep(1); } catch (const SequencerError& err) { cerr << "SequencerError exception. Error code: " << err.code() << " (" << err.qstrError() << ")" << endl; cerr << "Location: " << err.location() << endl; } } static Metronome* metronome = nullptr; void signalHandler(int sig) { if (sig == SIGINT) qDebug() << "Caught a SIGINT. Exiting"; else if (sig == SIGTERM) qDebug() << "Caught a SIGTERM. Exiting"; if (metronome != nullptr) { metronome->stop(); metronome->shutupSound(); } } int main(int argc, char **argv) { const QString PGM_NAME = QStringLiteral("drumstick-metronome"); const QString PGM_DESCRIPTION = QStringLiteral("ALSA based command line metronome"); const QString ERRORSTR = QStringLiteral("Fatal error from the ALSA sequencer. " "This usually happens when the kernel doesn't have ALSA support, " "or the device node (/dev/snd/seq) doesn't exists, " "or the kernel module (snd_seq) is not loaded. " "Please check your ALSA/MIDI configuration."); signal(SIGINT, signalHandler); signal(SIGTERM, signalHandler); QCoreApplication app(argc, argv); QCoreApplication::setApplicationName(PGM_NAME); QCoreApplication::setApplicationVersion(QStringLiteral(QT_STRINGIFY(VERSION))); QCommandLineParser parser; parser.setApplicationDescription(PGM_DESCRIPTION); auto helpOption = parser.addHelpOption(); auto versionOption = parser.addVersionOption(); QCommandLineOption portOption({"p","port"}, "Destination, MIDI port identifier.", "client:port"); parser.addOption(portOption); QCommandLineOption bpmOption({"b","bpm"}, "Tempo, in beats per minute (default=120).", "BPM", "120"); parser.addOption(bpmOption); parser.process(app); if (parser.isSet(versionOption) || parser.isSet(helpOption)) { return 0; } try { metronome = new Metronome(); if (parser.isSet(portOption)) { QString port = parser.value(portOption); metronome->subscribe(port); } else { cerr << "Destination Port is mandatory" << endl; parser.showHelp(); } QString bpm("120"); if (parser.isSet(bpmOption)) { bpm = parser.value(bpmOption); } metronome->play(bpm); } catch (const SequencerError& ex) { cerr << ERRORSTR << " Returned error was: " << ex.qstrError() << endl; } catch (...) { cerr << ERRORSTR << endl; } delete metronome; return 0; } drumstick-2.9.0/utils/metronome/metronome.h0000644000175000017500000000556414541630232020070 0ustar pedropedro/* Standard MIDI simple metronome Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef METRONOME_H #define METRONOME_H #include #include #include #include #include #include #include #include const int TEMPO_DEFAULT(120); const int NOTE_DURATION(10); const int RHYTHM_TS_NUM(4); const int RHYTHM_TS_DEN(4); const int METRONOME_CHANNEL(9); const int METRONOME_STRONG_NOTE(34); const int METRONOME_WEAK_NOTE(33); const int METRONOME_VELOCITY(100); const int METRONOME_PROGRAM(0); const int METRONOME_RESOLUTION(240); const int METRONOME_VOLUME(100); const int METRONOME_PAN(64); class Metronome : public QObject, public drumstick::ALSA::SequencerEventHandler { Q_OBJECT public: explicit Metronome(QObject *parent = nullptr); virtual ~Metronome(); void play(QString tempo); bool stopped(); void stop(); void subscribe(const QString& portName); void shutupSound(); void sendControlChange( int cc, int value ); void sendInitialControls(); void metronome_note(int note, int tick); void metronome_echo(int tick); void metronome_pattern(int tick); void metronome_event_output(drumstick::ALSA::SequencerEvent* ev); void metronome_schedule_event(drumstick::ALSA::SequencerEvent* ev, int tick, bool lb); void metronome_set_program(); void metronome_set_tempo(); void metronome_set_controls(); // SequencerEventHandler interface void handleSequencerEvent( drumstick::ALSA::SequencerEvent* ev ) override; private: int m_weak_note; int m_strong_note; int m_weak_velocity; int m_strong_velocity; int m_program; int m_channel; int m_volume; int m_pan; int m_resolution; int m_bpm; int m_ts_num; /* time signature: numerator */ int m_ts_div; /* time signature: denominator */ int m_noteDuration; int m_patternDuration; int m_portId; int m_queueId; int m_clientId; bool m_Stopped; QReadWriteLock m_mutex; drumstick::ALSA::MidiClient* m_Client; drumstick::ALSA::MidiPort* m_Port; drumstick::ALSA::MidiQueue* m_Queue; }; #endif /*METRONOME_H*/ drumstick-2.9.0/utils/metronome/metronome.pro0000644000175000017500000000051614541630232020431 0ustar pedropedroTEMPLATE = app TARGET = drumstick-metronome #QT += dbus CONFIG += c++11 cmdline qt thread exceptions static { CONFIG += link_prl } DESTDIR = ../../build/bin INCLUDEPATH += . ../../library/include LIBS = -L../../build/lib -ldrumstick-alsa -lasound include (../../global.pri) # Input HEADERS += metronome.h SOURCES += metronome.cpp drumstick-2.9.0/utils/metronome/CMakeLists.txt0000644000175000017500000000262214541630232020442 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(metronome_SRCS metronome.cpp metronome.h ) set(metronome_qtobject_SRCS metronome.h ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(metronome_moc_SRCS ${metronome_qtobject_SRCS}) else() qt_wrap_cpp(metronome_moc_SRCS ${metronome_qtobject_SRCS}) endif() add_executable(drumstick-metronome ${metronome_moc_SRCS} ${metronome_SRCS} ) target_link_libraries(drumstick-metronome PRIVATE Drumstick::ALSA Qt${QT_VERSION_MAJOR}::Core ) install(TARGETS drumstick-metronome RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) drumstick-2.9.0/utils/playsmf/0000755000175000017500000000000014541630232015346 5ustar pedropedrodrumstick-2.9.0/utils/playsmf/playsmf.cpp0000644000175000017500000002610414541630232017530 0ustar pedropedro/* Standard MIDI File player program Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "playsmf.h" #include #include #include #include #include #include #include #include #include #include DISABLE_WARNING_PUSH DISABLE_WARNING_DEPRECATED_DECLARATIONS #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) #define right Qt::right #define left Qt::left #define endl Qt::endl #endif QTextStream cout(stdout, QIODevice::WriteOnly); QTextStream cerr(stderr, QIODevice::WriteOnly); /* ********** * * Song class * ********** */ using namespace drumstick; using namespace ALSA; using namespace File; static inline bool eventLessThan(const SequencerEvent* s1, const SequencerEvent *s2) { return s1->getTick() < s2->getTick(); } void Song::sort() { std::sort(begin(), end(), eventLessThan); } void Song::clear() { while (!isEmpty()) delete takeFirst(); } Song::~Song() { clear(); } /* ************* * * PlaySMF class * ************* */ PlaySMF::PlaySMF() : m_division(-1), m_portId(-1), m_queueId(-1), m_initialTempo(-1), m_Stopped(true) { m_Client = new MidiClient(this); m_Client->open(); m_Client->setClientName("MIDI Player"); m_Port = new MidiPort(this); m_Port->attach( m_Client ); m_Port->setPortName("MIDI Player port"); m_Port->setCapability(SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ); m_Port->setPortType(SND_SEQ_PORT_TYPE_APPLICATION); m_Queue = m_Client->createQueue(); m_queueId = m_Queue->getId(); m_portId = m_Port->getPortId(); m_engine = new QSmf(this); connect(m_engine, &QSmf::signalSMFHeader, this, &PlaySMF::headerEvent); connect(m_engine, &QSmf::signalSMFNoteOn, this, &PlaySMF::noteOnEvent); connect(m_engine, &QSmf::signalSMFNoteOff, this, &PlaySMF::noteOffEvent); connect(m_engine, &QSmf::signalSMFKeyPress, this, &PlaySMF::keyPressEvent); connect(m_engine, &QSmf::signalSMFCtlChange, this, &PlaySMF::ctlChangeEvent); connect(m_engine, &QSmf::signalSMFPitchBend, this, &PlaySMF::pitchBendEvent); connect(m_engine, &QSmf::signalSMFProgram, this, &PlaySMF::programEvent); connect(m_engine, &QSmf::signalSMFChanPress, this, &PlaySMF::chanPressEvent); connect(m_engine, &QSmf::signalSMFSysex, this, &PlaySMF::sysexEvent); connect(m_engine, &QSmf::signalSMFText, this, &PlaySMF::textEvent); connect(m_engine, &QSmf::signalSMFTempo, this, &PlaySMF::tempoEvent); connect(m_engine, &QSmf::signalSMFTimeSig, this, &PlaySMF::timeSigEvent); connect(m_engine, &QSmf::signalSMFKeySig, this, &PlaySMF::keySigEvent); connect(m_engine, &QSmf::signalSMFError, this, &PlaySMF::errorHandler); } PlaySMF::~PlaySMF() { m_Port->detach(); m_Client->close(); } void PlaySMF::subscribe(const QString& portName) { try { qDebug() << "Trying to subscribe to " << portName.toLocal8Bit().data(); m_Port->subscribeTo(portName); } catch (const SequencerError& err) { cerr << "SequencerError exception. Error code: " << err.code() << " (" << err.qstrError() << ")" << endl; cerr << "Location: " << err.location() << endl; throw; } } bool PlaySMF::stopped() { QReadLocker locker(&m_mutex); return m_Stopped; } void PlaySMF::stop() { QWriteLocker locker(&m_mutex); m_Stopped = true; m_Client->dropOutput(); } void PlaySMF::shutupSound() { int channel; for (channel = 0; channel < 16; ++channel) { ControllerEvent ev(channel, MIDI_CTL_ALL_SOUNDS_OFF, 0); ev.setSource(static_cast(m_portId)); ev.setSubscribers(); ev.setDirect(); m_Client->outputDirect(&ev); } m_Client->drainOutput(); } void PlaySMF::appendEvent(SequencerEvent* ev) { long tick = m_engine->getCurrentTime(); ev->setSource(static_cast(m_portId)); if (ev->getSequencerType() != SND_SEQ_EVENT_TEMPO) { ev->setSubscribers(); } ev->scheduleTick(m_queueId, static_cast(tick), false); m_song.append(ev); } void PlaySMF::dump(const QString& chan, const QString& event, const QString& data) { cout << right << qSetFieldWidth(7) << m_engine->getCurrentTime(); cout << qSetFieldWidth(3) << chan; cout << qSetFieldWidth(0) << left << " "; cout << qSetFieldWidth(15) << event; cout << qSetFieldWidth(0) << " " << data << endl; } void PlaySMF::dumpStr(const QString& event, const QString& data) { cout << right << qSetFieldWidth(7) << m_engine->getCurrentTime(); cout << qSetFieldWidth(3) << "--"; cout << qSetFieldWidth(0) << left << " "; cout << qSetFieldWidth(15) << event; cout << qSetFieldWidth(0) << " " << data << endl; } void PlaySMF::headerEvent(int format, int ntrks, int division) { m_division = division; dumpStr("SMF Header", QString("Format=%1, Tracks=%2, Division=%3"). arg(format).arg(ntrks).arg(division)); } void PlaySMF::noteOnEvent(int chan, int pitch, int vol) { NoteOnEvent* ev = new NoteOnEvent (chan, pitch, vol); appendEvent(ev); } void PlaySMF::noteOffEvent(int chan, int pitch, int vol) { SequencerEvent* ev = new NoteOffEvent(chan, pitch, vol); appendEvent(ev); } void PlaySMF::keyPressEvent(int chan, int pitch, int press) { SequencerEvent* ev = new KeyPressEvent (chan, pitch, press); appendEvent(ev); } void PlaySMF::ctlChangeEvent(int chan, int ctl, int value) { SequencerEvent* ev = new ControllerEvent (chan, ctl, value); appendEvent(ev); } void PlaySMF::pitchBendEvent(int chan, int value) { SequencerEvent* ev = new PitchBendEvent (chan, value); appendEvent(ev); } void PlaySMF::programEvent(int chan, int patch) { SequencerEvent* ev = new ProgramChangeEvent (chan, patch); appendEvent(ev); } void PlaySMF::chanPressEvent(int chan, int press) { SequencerEvent* ev = new ChanPressEvent (chan, press); appendEvent(ev); } void PlaySMF::sysexEvent(const QByteArray& data) { SysExEvent* ev = new SysExEvent(data); appendEvent(ev); } void PlaySMF::textEvent(int typ, const QString& data) { dumpStr(QString("Text (%1)").arg(typ), data); } void PlaySMF::timeSigEvent(int b0, int b1, int b2, int b3) { dump("--", "Time Signature", QString("%1, %2, %3, %4").arg(b0).arg(b1).arg(b2).arg(b3)); } void PlaySMF::keySigEvent(int b0, int b1) { dump("--", "Key Signature", QString("%1, %2").arg(b0).arg(b1)); } void PlaySMF::tempoEvent(int tempo) { if ( m_initialTempo < 0 ) { m_initialTempo = tempo; } TempoEvent* ev = new TempoEvent(m_queueId, tempo); appendEvent(ev); } void PlaySMF::errorHandler(const QString& errorStr) { cout << "*** Warning! " << errorStr << " at file offset " << m_engine->getFilePos() << endl; } void PlaySMF::play(QString fileName) { cout << "Reading song: " << fileName << endl; cout << "___time ch event__________ data____" << endl; m_engine->readFromFile(fileName); m_song.sort(); m_Client->setPoolOutput(100); QueueTempo firstTempo = m_Queue->getTempo(); firstTempo.setPPQ(m_division); if (m_initialTempo > 0) firstTempo.setTempo(static_cast(m_initialTempo)); m_Queue->setTempo(firstTempo); m_Client->drainOutput(); cout << "Starting playback" << endl; cout << "Press Ctrl+C to exit" << endl; try { QListIterator i(m_song); m_Stopped = false; m_Queue->start(); while (!stopped() && i.hasNext()) { //m_Client->outputDirect(i.next()); m_Client->output(i.next()); } if (stopped()) { m_Queue->clear(); shutupSound(); } else { m_Client->drainOutput(); m_Client->synchronizeOutput(); } m_Queue->stop(); } catch (const SequencerError& err) { cerr << "SequencerError exception. Error code: " << err.code() << " (" << err.qstrError() << ")" << endl; cerr << "Location: " << err.location() << endl; throw; } } static PlaySMF* player = nullptr; void signalHandler(int sig) { if (sig == SIGINT) qDebug() << "Caught a SIGINT. Exiting"; else if (sig == SIGTERM) qDebug() << "Caught a SIGTERM. Exiting"; if (player != nullptr) player->stop(); } int main(int argc, char **argv) { const QString PGM_NAME = QStringLiteral("drumstick-playsmf"); const QString PGM_DESCRIPTION = QStringLiteral("Drumstick command line MIDI file player"); const QString ERRORSTR = QStringLiteral("Fatal error from the ALSA sequencer. " "This usually happens when the kernel doesn't have ALSA support, " "or the device node (/dev/snd/seq) doesn't exists, " "or the kernel module (snd_seq) is not loaded. " "Please check your ALSA/MIDI configuration."); signal(SIGINT, signalHandler); signal(SIGTERM, signalHandler); QCoreApplication app(argc, argv); QCoreApplication::setApplicationName(PGM_NAME); QCoreApplication::setApplicationVersion(QStringLiteral(QT_STRINGIFY(VERSION))); QCommandLineParser parser; parser.setApplicationDescription(PGM_DESCRIPTION); auto helpOption = parser.addHelpOption(); auto versionOption = parser.addVersionOption(); QCommandLineOption portOption({"p","port"}, "Destination, MIDI port.", "client:port"); parser.addOption(portOption); parser.addPositionalArgument("file", "Input SMF File(s).", "files..."); parser.process(app); if (parser.isSet(versionOption) || parser.isSet(helpOption)) { return 0; } try { player = new PlaySMF(); if (parser.isSet(portOption)) { QString port = parser.value(portOption); player->subscribe(port); } else { cerr << "Port argument is missing" << endl; parser.showHelp(); } QStringList files = parser.positionalArguments(); if (files.isEmpty()) { cerr << "No input files" << endl; parser.showHelp(); } foreach(const QString& f, files) { QFileInfo file(f); if (file.exists()) player->play(file.canonicalFilePath()); } } catch (const SequencerError& ex) { cerr << ERRORSTR << " Returned error was: " << ex.qstrError() << endl; } catch (...) { cerr << ERRORSTR << endl; } delete player; return 0; } DISABLE_WARNING_POP drumstick-2.9.0/utils/playsmf/playsmf.pro0000644000175000017500000000053114541630232017542 0ustar pedropedroTEMPLATE = app TARGET = drumstick-playsmf #QT += dbus CONFIG += c++11 cmdline qt thread exceptions static { CONFIG += link_prl } DESTDIR = ../../build/bin INCLUDEPATH += . ../../library/include LIBS = -L../../build/lib -ldrumstick-alsa -ldrumstick-file -lasound include (../../global.pri) # Input HEADERS += playsmf.h SOURCES += playsmf.cpp drumstick-2.9.0/utils/playsmf/CMakeLists.txt0000644000175000017500000000300414541630232020103 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(playsmf_SRCS playsmf.cpp playsmf.h ) set(playsmf_qtobject_SRCS playsmf.h ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_wrap_cpp(playsmf_moc_SRCS ${playsmf_qtobject_SRCS}) else() qt_wrap_cpp(playsmf_moc_SRCS ${playsmf_qtobject_SRCS}) endif() add_executable(drumstick-playsmf ${playsmf_moc_SRCS} ${playsmf_SRCS} ) target_link_libraries(drumstick-playsmf PRIVATE Drumstick::ALSA Drumstick::File Qt${QT_VERSION_MAJOR}::Core ) if(QT_VERSION VERSION_GREATER_EQUAL 6.0.0) target_link_libraries(drumstick-playsmf PRIVATE Qt6::Core5Compat) endif() install(TARGETS drumstick-playsmf RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) drumstick-2.9.0/utils/playsmf/playsmf.h0000644000175000017500000000513714541630232017200 0ustar pedropedro/* Standard MIDI File player program Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PLAYSMF_H_ #define PLAYSMF_H_ #include #include #include #include #include #include #include #include #include class Song : public QList { public: virtual ~Song(); void sort(); void clear(); }; class PlaySMF : public QObject { Q_OBJECT public: PlaySMF(); virtual ~PlaySMF(); void play(QString fileName); bool stopped(); void stop(); void appendEvent(drumstick::ALSA::SequencerEvent* ev); void subscribe(const QString& portName); void dump(const QString& chan, const QString& event, const QString& data); void dumpStr(const QString& event, const QString& data); void shutupSound(); public Q_SLOTS: void headerEvent(int format, int ntrks, int division); void noteOnEvent(int chan, int pitch, int vol); void noteOffEvent(int chan, int pitch, int vol); void keyPressEvent(int chan, int pitch, int press); void ctlChangeEvent(int chan, int ctl, int value); void pitchBendEvent(int chan, int value); void programEvent(int chan, int patch); void chanPressEvent(int chan, int press); void sysexEvent(const QByteArray& data); void textEvent(int typ, const QString& data); void tempoEvent(int tempo); void timeSigEvent(int b0, int b1, int b2, int b3); void keySigEvent(int b0, int b1); void errorHandler(const QString& errorStr); private: int m_division; int m_portId; int m_queueId; int m_initialTempo; bool m_Stopped; QReadWriteLock m_mutex; Song m_song; drumstick::File::QSmf* m_engine; drumstick::ALSA::MidiClient* m_Client; drumstick::ALSA::MidiPort* m_Port; drumstick::ALSA::MidiQueue* m_Queue; }; #endif /*PLAYSMF_H_*/ drumstick-2.9.0/utils/sysinfo/0000755000175000017500000000000014541630232015365 5ustar pedropedrodrumstick-2.9.0/utils/sysinfo/sysinfo.cpp0000644000175000017500000002251514541630232017570 0ustar pedropedro/* MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) #define right Qt::right #define left Qt::left #define endl Qt::endl #define hex Qt::hex #define dec Qt::dec #endif QString PGM_NAME = QStringLiteral("drumstick-sysinfo"); QString PGM_DESCRIPTION = QStringLiteral("ALSA Sequencer System Info"); QTextStream cout(stdout, QIODevice::WriteOnly); QTextStream cerr(stderr, QIODevice::WriteOnly); using namespace drumstick::ALSA; void queryTimers() { cout << endl << "ALSA Timers" << endl; TimerQuery* query = new TimerQuery("hw", 0); cout << "type___ Name________________ c/s/C/D/S Freq." << endl; TimerIdList lst = query->getTimers(); TimerIdList::ConstIterator it; for( it = lst.constBegin(); it != lst.constEnd(); ++it ) { TimerId id = *it; try { Timer* timer = new Timer(id, SND_TIMER_OPEN_NONBLOCK); TimerInfo info = timer->getTimerInfo(); cout << qSetFieldWidth(8) << left << info.getId() << qSetFieldWidth(20) << left << info.getName().leftJustified(20, ' ', true) << qSetFieldWidth(0) << " " << id.getClass() << "/" << id.getSlaveClass() << "/" << id.getCard() << "/" << id.getDevice() << "/" << id.getSubdevice() << " "; if( info.isSlave() ) { cout << "SLAVE"; } else { long freq = info.getFrequency(); cout << freq << " Hz"; } cout << endl; delete timer; } catch (const SequencerError& err) { cerr << "Error opening timer:" << err.qstrError(); } } delete query; } void queryQueues(MidiClient* c) { cout << endl << "ALSA Queues" << endl; cout << "id Queue_Name__________ Timer_Name__________ owner status " << " state PPQ Tempo BPM Ticks Time" << endl; QList queues = c->getAvailableQueues(); foreach( int q, queues ) { MidiQueue* queue = new MidiQueue(c, q); if (queue != nullptr) { QueueInfo qinfo = queue->getInfo(); QueueStatus qsts = queue->getStatus(); QueueTempo qtmp = queue->getTempo(); QueueTimer qtmr = queue->getTimer(); TimerId tid(qtmr.getId()); QString tname; try { Timer* timer = new Timer(tid, SND_TIMER_OPEN_NONBLOCK); TimerInfo tinfo = timer->getTimerInfo(); tname = tinfo.getName(); delete timer; } catch (...) { tname = "inaccessible"; } cout << qSetFieldWidth(3) << left << qinfo.getId() << qSetFieldWidth(20) << qinfo.getName().leftJustified(20, ' ', true) << qSetFieldWidth(0) << " " << qSetFieldWidth(20) << tname.leftJustified(20, ' ', true) << qSetFieldWidth(6) << right << qinfo.getOwner() << qSetFieldWidth(7) << (qinfo.isLocked() ? "locked" : "free") << qSetFieldWidth(8) << (qsts.isRunning() ? "running" : "stopped") << qSetFieldWidth(4) << qtmp.getPPQ() << qSetFieldWidth(7) << qtmp.getRealBPM() << qSetFieldWidth(4) << qtmp.getNominalBPM() << qSetFieldWidth(8) << qsts.getTickTime() << qSetFieldWidth(0) << " " << qsts.getClockTime() << endl; delete queue; } } } QString clientTypeName(snd_seq_client_type_t ctype) { if (ctype == SND_SEQ_USER_CLIENT) return "User"; if (ctype == SND_SEQ_KERNEL_CLIENT) return "Kernel"; return "Unknown"; } QString portTypeNames(int ptype) { QStringList lst; if ((ptype & SND_SEQ_PORT_TYPE_HARDWARE) != 0) lst << "Hardware"; if ((ptype & SND_SEQ_PORT_TYPE_SOFTWARE) != 0) lst << "Software"; if ((ptype & SND_SEQ_PORT_TYPE_PORT) != 0) lst << "Port"; if ((ptype & SND_SEQ_PORT_TYPE_DIRECT_SAMPLE) != 0) lst << "Direct Sample"; if ((ptype & SND_SEQ_PORT_TYPE_MIDI_GENERIC) != 0) lst << "MIDI Generic"; if ((ptype & SND_SEQ_PORT_TYPE_MIDI_GM) != 0) lst << "GM"; if ((ptype & SND_SEQ_PORT_TYPE_MIDI_GM2) != 0) lst << "GM2"; if ((ptype & SND_SEQ_PORT_TYPE_MIDI_GS) != 0) lst << "GS"; if ((ptype & SND_SEQ_PORT_TYPE_MIDI_MT32) != 0) lst << "MT32"; if ((ptype & SND_SEQ_PORT_TYPE_MIDI_XG) != 0) lst << "XG"; if ((ptype & SND_SEQ_PORT_TYPE_SAMPLE) != 0) lst << "Sample"; if ((ptype & SND_SEQ_PORT_TYPE_SPECIFIC) != 0) lst << "Specific"; if ((ptype & SND_SEQ_PORT_TYPE_SYNTH) != 0) lst << "Synth"; if ((ptype & SND_SEQ_PORT_TYPE_APPLICATION) != 0) lst << "Application"; if ((ptype & SND_SEQ_PORT_TYPE_SYNTHESIZER) != 0) lst << "Synthesizer"; return " (" + lst.join(", ") + ")"; } QString subsNames(SubscribersList& subs) { QStringList lst; foreach( Subscriber s, subs ) { QString sname = QString("%1:%2").arg(static_cast(s.getAddr()->client)) .arg(static_cast(s.getAddr()->port)); lst << sname; } return lst.join(", "); } void queryClients(MidiClient* c) { cout << endl << "ALSA Sequencer clients" << endl; ClientInfoList clients = c->getAvailableClients(); foreach( ClientInfo cinfo, clients ) { PortInfoList plist = cinfo.getPorts(); cout << "Client " << qSetFieldWidth(4) << left << cinfo.getClientId() << qSetFieldWidth(0) << " : \"" << cinfo.getName() << "\" [" << clientTypeName(cinfo.getClientType()) << "]" << endl; foreach( PortInfo pinfo, plist ) { SubscribersList to = pinfo.getReadSubscribers(); SubscribersList from = pinfo.getWriteSubscribers(); cout << " Port " << qSetFieldWidth(4) << right << pinfo.getPort() << qSetFieldWidth(0) << " : \"" << pinfo.getName() << "\"" << (pinfo.getType() != 0 ? portTypeNames(pinfo.getType()) : "") << endl; if ( from.count() > 0 ) cout << " Connected From: " << subsNames(from) << endl; if ( to.count() > 0 ) cout << " Connecting To: " << subsNames(to) << endl; } } } void systemInfo() { MidiClient* client = new MidiClient(); client->open(); client->setClientName(PGM_NAME); SystemInfo info = client->getSystemInfo(); cout << PGM_DESCRIPTION << ", version: "<< QStringLiteral(QT_STRINGIFY(VERSION)) << endl; cout << "Compiled ALSA library: " << getCompiledALSALibraryVersion() << endl; cout << "Runtime ALSA library: " << getRuntimeALSALibraryVersion() << endl; cout << "Runtime ALSA drivers: " << getRuntimeALSADriverVersion() << endl; cout << "Numeric ALSA compiled library: " << hex << SND_LIB_VERSION << endl; cout << "Numeric ALSA runtime library: " << getRuntimeALSALibraryNumber() << endl; cout << "Numeric ALSA runtime driver: " << getRuntimeALSADriverNumber() << endl; cout << "Max Clients: " << dec << info.getMaxClients() << endl; cout << "Max Ports: " << info.getMaxPorts() << endl; cout << "Max Queues: " << info.getMaxQueues() << endl; cout << "Max Channels: " << info.getMaxChannels() << endl; cout << "Current Queues: " << info.getCurrentQueues() << endl; cout << "Current Clients: " << info.getCurrentClients() << endl; queryTimers(); if (info.getCurrentQueues() > 0) queryQueues(client); if (info.getCurrentClients() > 0) queryClients(client); delete client; } int main(int argc, char **argv) { const QString ERRORSTR = QStringLiteral("Fatal error from the ALSA sequencer. " "This usually happens when the kernel doesn't have ALSA support, " "or the device node (/dev/snd/seq) doesn't exists, " "or the kernel module (snd_seq) is not loaded. " "Please check your ALSA/MIDI configuration."); QCoreApplication app(argc, argv); QCoreApplication::setApplicationName(PGM_NAME); QCoreApplication::setApplicationVersion(QStringLiteral(QT_STRINGIFY(VERSION))); QCommandLineParser parser; parser.setApplicationDescription(PGM_DESCRIPTION); auto helpOption = parser.addHelpOption(); auto versionOption = parser.addVersionOption(); parser.process(app); if (parser.isSet(versionOption) || parser.isSet(helpOption)) { return 0; } try { systemInfo(); } catch (const SequencerError& ex) { cerr << ERRORSTR << " Returned error was: " << ex.qstrError() << endl; } catch (...) { cerr << ERRORSTR << endl; } return 0; } drumstick-2.9.0/utils/sysinfo/sysinfo.pro0000644000175000017500000000054714541630232017607 0ustar pedropedroTEMPLATE = app TARGET = drumstick-sysinfo #QT += dbus CONFIG += c++11 cmdline qt thread exceptions static { CONFIG += link_prl } DESTDIR = ../../build/bin INCLUDEPATH += . ../../library/include DEPENDPATH += . ../../library ../../library/include LIBS = -L../../build/lib -ldrumstick-alsa -lasound include (../../global.pri) # Input SOURCES += sysinfo.cpp drumstick-2.9.0/utils/sysinfo/CMakeLists.txt0000644000175000017500000000217214541630232020127 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(sysinfo_SRCS sysinfo.cpp ) add_executable(drumstick-sysinfo ${sysinfo_SRCS} ) target_link_libraries(drumstick-sysinfo PRIVATE Drumstick::ALSA Qt${QT_VERSION_MAJOR}::Core ) install(TARGETS drumstick-sysinfo RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) drumstick-2.9.0/utils/utils.pro0000644000175000017500000000046714541630232015564 0ustar pedropedroTEMPLATE = subdirs include (../global.pri) DEFINES += VERSION=$$VERSION SUBDIRS += \ dumprmi \ dumpsmf \ dumpwrk \ vpiano linux { SUBDIRS += \ drumgrid \ dumpmid \ guiplayer \ metronome \ playsmf \ sysinfo } macx { OTHER_FILES += Info.plist.app } drumstick-2.9.0/utils/vpiano/0000755000175000017500000000000014541630232015167 5ustar pedropedrodrumstick-2.9.0/utils/vpiano/connections.cpp0000644000175000017500000002127214541630232020221 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include "vpianosettings.h" #include "connections.h" using namespace drumstick::rt; Connections::Connections(QWidget *parent) : QDialog(parent), m_settingsChanged(false), m_midiIn(nullptr), m_savedIn(nullptr), m_midiOut(nullptr), m_savedOut(nullptr) { ui.setupUi(this); ui.m_advanced->setChecked(VPianoSettings::instance()->advanced()); ui.m_thru->setChecked(VPianoSettings::instance()->midiThru()); connect(ui.m_advanced, &QCheckBox::clicked, this, &Connections::clickedAdvanced); connect(ui.m_inputBackends, QOverload::of(&QComboBox::currentIndexChanged), this, &Connections::refreshInputs); connect(ui.m_outputBackends, QOverload::of(&QComboBox::currentIndexChanged), this, &Connections::refreshOutputs); connect(ui.btnInputDriverCfg, &QToolButton::clicked, this, &Connections::configureInputDriver); connect(ui.btnOutputDriverCfg, &QToolButton::clicked, this, &Connections::configureOutputDriver); } void Connections::setInput(MIDIInput *in) { m_savedIn = m_midiIn = in; m_connIn = in->currentConnection(); } void Connections::setOutput(MIDIOutput *out) { m_savedOut = m_midiOut = out; m_connOut = out->currentConnection(); } void Connections::setInputs(QList ins) { ui.m_inputBackends->disconnect(); ui.m_inputBackends->clear(); foreach(MIDIInput *i, ins) { ui.m_inputBackends->addItem(i->backendName(), QVariant::fromValue(i)); } connect(ui.m_inputBackends, QOverload::of(&QComboBox::currentIndexChanged), this, &Connections::refreshInputs); } void Connections::setOutputs(QList outs) { ui.m_outputBackends->disconnect(); foreach(MIDIOutput *o, outs) { ui.m_outputBackends->addItem(o->backendName(), QVariant::fromValue(o)); } connect(ui.m_outputBackends, QOverload::of(&QComboBox::currentIndexChanged), this, &Connections::refreshOutputs); } MIDIInput *Connections::getInput() { return m_midiIn; } MIDIOutput *Connections::getOutput() { return m_midiOut; } void Connections::reopenDrivers() { drumstick::widgets::SettingsFactory settings; if (m_midiOut != nullptr) { if (m_connOut != m_midiOut->currentConnection() || m_settingsChanged) { m_midiOut->close(); if (!m_connOut.first.isEmpty()) { m_midiOut->initialize(settings.getQSettings()); m_midiOut->open(m_connOut); auto metaObj = m_midiOut->metaObject(); if ((metaObj->indexOfProperty("status") != -1) && (metaObj->indexOfProperty("diagnostics") != -1)) { auto status = m_midiOut->property("status"); if (status.isValid() && !status.toBool()) { auto diagnostics = m_midiOut->property("diagnostics"); if (diagnostics.isValid()) { auto text = diagnostics.toStringList().join(QChar::LineFeed).trimmed(); QMessageBox::warning(this, tr("MIDI Output"), text); } } } } } } if (m_midiIn != nullptr) { if (m_connIn != m_midiIn->currentConnection() || m_settingsChanged) { m_midiIn->close(); m_midiIn->initialize(settings.getQSettings()); if (!m_connIn.first.isEmpty()) { m_midiIn->open(m_connIn); auto metaObj = m_midiIn->metaObject(); if ((metaObj->indexOfProperty("status") != -1) && (metaObj->indexOfProperty("diagnostics") != -1)) { auto status = m_midiIn->property("status"); if (status.isValid() && !status.toBool()) { auto diagnostics = m_midiIn->property("diagnostics"); if (diagnostics.isValid()) { auto text = diagnostics.toStringList().join(QChar::LineFeed).trimmed(); QMessageBox::warning(this, tr("MIDI Input"), text); } } } } } if (m_midiOut != nullptr) { m_midiIn->setMIDIThruDevice(m_midiOut); m_midiIn->enableMIDIThru(VPianoSettings::instance()->midiThru()); } } m_settingsChanged = false; } void Connections::accept() { m_connIn = ui.m_inputPorts->currentData().value(); m_connOut = ui.m_outputPorts->currentData().value(); VPianoSettings::instance()->setAdvanced(ui.m_advanced->isChecked()); VPianoSettings::instance()->setMidiThru(ui.m_thru->isChecked()); reopenDrivers(); VPianoSettings::instance()->setLastOutputBackend(m_midiOut->backendName()); VPianoSettings::instance()->setLastOutputConnection(m_midiOut->currentConnection().first); VPianoSettings::instance()->setLastInputBackend(m_midiIn->backendName()); VPianoSettings::instance()->setLastInputConnection(m_midiIn->currentConnection().first); QDialog::accept(); } void Connections::reject() { m_midiIn = m_savedIn; m_midiOut = m_savedOut; reopenDrivers(); QDialog::reject(); } void Connections::refresh() { if (m_midiIn != nullptr) { ui.m_inputBackends->setCurrentText(m_midiIn->backendName()); refreshInputs(ui.m_inputBackends->currentIndex()); } if (m_midiOut != nullptr) { ui.m_outputBackends->setCurrentText(m_midiOut->backendName()); refreshOutputs(ui.m_outputBackends->currentIndex()); } } void Connections::refreshInputs(int index) { QString id = ui.m_inputBackends->itemText(index); ui.btnInputDriverCfg->setEnabled(drumstick::widgets::inputDriverIsConfigurable(id)); if (m_midiIn != nullptr && m_midiIn->backendName() != id) { m_midiIn->close(); m_midiIn = ui.m_inputBackends->itemData(index).value(); } ui.m_inputPorts->clear(); if (m_midiIn != nullptr) { const QList conns = m_midiIn->connections(ui.m_advanced->isChecked()); for (const MIDIConnection& conn : conns) { ui.m_inputPorts->addItem(conn.first, QVariant::fromValue(conn)); } QString strconn = m_midiIn->currentConnection().first; if (strconn.isEmpty() && !conns.isEmpty()) { strconn = conns.first().first; } ui.m_inputPorts->setCurrentText(strconn); } } void Connections::refreshOutputs(int index) { QString id = ui.m_outputBackends->itemText(index); ui.btnOutputDriverCfg->setEnabled(drumstick::widgets::outputDriverIsConfigurable(id)); if (m_midiOut != nullptr && m_midiOut->backendName() != id) { m_midiOut->close(); m_midiOut = ui.m_outputBackends->itemData(index).value(); } ui.m_outputPorts->clear(); if (m_midiOut != nullptr) { const QList conns = m_midiOut->connections(ui.m_advanced->isChecked()); for(const MIDIConnection& conn : conns) { ui.m_outputPorts->addItem(conn.first, QVariant::fromValue(conn)); } QString strconn = m_midiOut->currentConnection().first; if (strconn.isEmpty() && !conns.isEmpty()) { strconn = conns.first().first; } ui.m_outputPorts->setCurrentText(strconn); } } void Connections::clickedAdvanced(bool value) { Q_UNUSED(value) refresh(); } void Connections::configureInputDriver() { QString driver = ui.m_inputBackends->currentText(); if (drumstick::widgets::inputDriverIsConfigurable(driver)) { m_settingsChanged |= drumstick::widgets::configureInputDriver(driver, this); } } void Connections::configureOutputDriver() { QString driver = ui.m_outputBackends->currentText(); if (drumstick::widgets::outputDriverIsConfigurable(driver)) { m_settingsChanged |= drumstick::widgets::configureOutputDriver(driver, this); } } drumstick-2.9.0/utils/vpiano/connections.ui0000644000175000017500000000664314541630232020061 0ustar pedropedro ConnectionsClass 0 0 289 253 Connections MIDI IN Enable MIDI THRU MIDI OUT false ... Show advanced connections Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok false ... m_inputBackends btnInputDriverCfg m_inputPorts m_thru m_outputBackends btnOutputDriverCfg m_outputPorts m_advanced buttonBox accepted() ConnectionsClass accept() 264 303 283 112 buttonBox rejected() ConnectionsClass reject() 65 303 93 74 drumstick-2.9.0/utils/vpiano/drumstick-vpiano.desktop0000644000175000017500000000036714541630232022067 0ustar pedropedro[Desktop Entry] Name=Drumstick Virtual Piano Exec=drumstick-vpiano Icon=drumstick Terminal=false Type=Application Categories=AudioVideo;Audio;Midi;Education;Music; Keywords=Music;Midi;Piano;Virtual; Comment=Virtual Piano Comment[es]=Piano virtual drumstick-2.9.0/utils/vpiano/drumstick-vpiano_en.ts0000644000175000017500000004437714541630232021537 0ustar pedropedro About Revision AboutClass About <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Virtual Piano %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:16pt; font-weight:600;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-weight:600;">Using Qt version: %QT_VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> Connections MIDI Output MIDI Input ConnectionsClass Connections MIDI IN Enable MIDI THRU MIDI OUT ... Show advanced connections PreferencesClass Preferences Output Channel Number of keys Velocity Input Channel Base octave Starting Key C C# D D# E F F# G G# A A# B VPiano Drumstick Virtual Piano File Edit Help View Show Note Names Black Keys Names Names Orientation Central Octave C Note Name Note Names &Exit &Preferences &Connections About About Qt Never When Activated Always Sharps Flats Nothing Horizontal Vertical Automatic Names Font Minimal C3 C4 C5 Standard Custom with Sharps Custom with Flats C Inverted Keys Color Raw Computer Keyboard Computer Keyboard Input Mouse Input Touch Screen Input Octave Subscript Designation Font to display note names Custom Note Names Names: main Drumstick Simple Virtual Piano Copyright (C) 2006-2023 Pedro Lopez-Cabanillas This program comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome to redistribute it under certain conditions; see the LICENSE file for details. Portable settings mode Fatal error from a MIDI backend. drumstick-2.9.0/utils/vpiano/drumstick-vpiano_es.ts0000644000175000017500000006154614541630232021541 0ustar pedropedro About Revision Revisión AboutClass About Acerca de <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Virtual Piano %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:16pt; font-weight:600;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-weight:600;">Using Qt version: %QT_VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Virtual Piano %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:16pt; font-weight:600;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-weight:600;">Using Qt version: %QT_VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> Connections MIDI Output Salida MIDI MIDI Input Entrada MIDI ConnectionsClass Connections Conexiones MIDI IN MIDI IN Enable MIDI THRU Habilitar MIDI THRU MIDI OUT MIDI OUT ... ... Show advanced connections Mostrar conexiones avanzadas PreferencesClass Preferences Preferencias Output Channel Canal de salida Number of keys Número de teclas Velocity Velocidad Input Channel Canal de entrada Base octave Octava base Starting Key Tecla inicial C do C# do# D re D# re# E mi F fa F# fa# G sol G# sol# A la A# la# B si QCoreApplication Portable settings mode Modo de ajustes portables QObject Drumstick Simple Virtual Piano Copyright (C) 2006-2023 Pedro López-Cabanillas This program comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome to redistribute it under certain conditions; see the LICENSE file for details. Drumstick Piano Virtual Sencillo Copyright (C) 2006-2023 Pedro López-Cabanillas Este programa se ofrece SIN GARANTÍA ALGUNA. Este programa es software libre, y usted puede redistribuirlo bajo ciertas condiciones. Portable settings mode Modo de ajustes portables VPiano Drumstick Virtual Piano Piano virtual Drumstick File Archivo Edit Edición Help Ayuda View Ver Show Note Names Mostrar nombres de notas Black Keys Names Nombres de las teclas negras Names Orientation Orientación de los nombres Central Octave Name Nombre de la octava central Central Octave C Note Name Nombre del Do de la octava central Note Names Nombres de notas &Exit &Salir &Preferences &Preferencias &Connections &Conexiones About Acerca de About Qt Acerca de Qt Never Nunca When Activated Cuando se activen Always Siempre Sharps Sostenidos Flats Bemoles Nothing Nada Horizontal Horizontal Vertical Vertical Automatic Automática Names Font Fuente para nombres Minimal Mínimo C3 do3 C4 do4 C5 do5 Standard Estándar Custom with Sharps Personalizadas con sostenidos Custom with Flats Personalizadas con bemoles C do Inverted Keys Color Teclas de color invertido Raw Computer Keyboard Teclado de ordenador de bajo nivel Computer Keyboard Input Entrada por teclado de ordenador Mouse Input Entrada por ratón Touch Screen Input Entrada por pantalla táctil Octave Subscript Designation Designación de subíndice de octava Font to display note names Fuente para mostrar nombres de notas Custom Note Names Nombres personalizados de notas Names: Nombres: main Drumstick Simple Virtual Piano Copyright (C) 2006-2023 Pedro Lopez-Cabanillas This program comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome to redistribute it under certain conditions; see the LICENSE file for details. Drumstick Simple Virtual Piano Copyright (C) 2006-2023 Pedro López-Cabanillas This program comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome to redistribute it under certain conditions; see the LICENSE file for details. Drumstick Piano Virtual Sencillo Copyright (C) 2006-2023 Pedro Lopez-Cabanillas Este programa se ofrece SIN GARANTÍA ALGUNA. Este programa es software libre, y usted puede redistribuirlo bajo ciertas condiciones; vea el archivo LICENSE para detalles. Drumstick Simple Virtual Piano Copyright (C) 2006-2023 Pedro Lopez-Cabanillas This program comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome to redistribute it under certain conditions; see the LICENSE file for details. Drumstick Piano Virtual Sencillo Copyright (C) 2006-2023 Pedro Lopez-Cabanillas Este programa se ofrece SIN GARANTÍA ALGUNA. Este programa es software libre, y usted puede redistribuirlo bajo ciertas condiciones; vea el archivo LICENSE para detalles. Portable settings mode Modo de ajustes portables Fatal error from a MIDI backend. Error grave en el soporte MIDI. drumstick-2.9.0/utils/vpiano/drumstick-vpiano_it.ts0000644000175000017500000005547414541630232021551 0ustar pedropedro About Revision Revisione AboutClass About Informazioni <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Virtual Piano %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:16pt; font-weight:600;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-weight:600;">Using Qt version: %QT_VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Drum Grid %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Versione Qt: %QT_VERSION%</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Applicazione di esempio per la </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">libreria C++ Drumstick MIDI Sequencer</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">Questo programma è free software; lo si può ridistribuire e/o modificare in base ai termini della GNU General Public License come pubblicata dalla Free Software Foundation; sia nella versione 3 della Licenza, o (a scelta) in qualsiasi versione successiva.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">Questo programma è distribuito con la speranza che sia utile, ma SENZA QUALUNQUE GARANZIA; inclusa l'implicita garanzia di COMMERCIABILITÀ o di ADEGUATEZZA PER UNO SCOPO PARTICOLARE. Vedere la GNU General Public License per ulteriori dettagli.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">Dovreste avere ricevuto una copia della GNU General Public License insieme con questo programma. In caso contrario, vedere </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> Connections MIDI Output Uscita MIDI MIDI Input Ingresso MIDI ConnectionsClass Connections Connessioni MIDI IN Ingresso MIDI Enable MIDI THRU Abilita MIDI THRU MIDI OUT Uscita MIDI ... ... Show advanced connections Mostra connessioni avanzate PreferencesClass Preferences Preferenze Output Channel Canale di uscita Number of keys Numero di tasti Velocity Velocità Input Channel Canale di ingresso Base octave Ottava base Starting Key Tasto iniziale C Do C# Do# D Re D# Re# E F Fa F# Fa# G Sol G# Sol# A La A# La# B Si VPiano Drumstick Virtual Piano Drumstick Tastiera Virtuale File File Edit Modifica Help Aiuto View Visualizza Show Note Names Mostra i nomi delle note Black Keys Names Mostra i nomi dei tasti neri Names Orientation Orientamento dei nomi Central Octave C Note Name Nome del Do dell'ottava centrale Note Names Nomi delle note &Exit &Esci &Preferences &Preferenze &Connections &Connessioni About Informazioni About Qt Informazioni su Qt Never Mai When Activated Quando attivato Always Sempre Sharps Diesis Flats Bemolle Nothing Nulla Horizontal Orizzontale Vertical Verticale Automatic Automatico Names Font Nome del carattere Minimal Minimo C3 Do3 C4 Do4 C5 Do5 Standard Standard Custom with Sharps Personalizzato con diesis Custom with Flats Personalizzato con bemolle C Do Inverted Keys Color Tasti con colori invertiti Raw Computer Keyboard Tastiera del computer grezza Computer Keyboard Input Inserimento con la tastiera del computer Mouse Input Inserimento con il mouse Touch Screen Input Inserimento con il touch screen Octave Subscript Designation Nome dell'ottava in pedice Font to display note names Carattere per mostrare i nomi delle note Custom Note Names Nomi delle note personalizzati Names: Nomi: main Drumstick Simple Virtual Piano Copyright (C) 2006-2023 Pedro Lopez-Cabanillas This program comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome to redistribute it under certain conditions; see the LICENSE file for details. Semplice Tastiera Virtuale per Drumstick Copyright (C) 2006-2023 Pedro Lopez-Cabanillas Questo programma non ha ASSOLUTAMENTE ALCUNA GARANZIA. Questo è free software ed è liberamente ridistribuibile, a certe condizioni; vedere il file LICENSE per i dettagli. Portable settings mode Impostazioni modalità portabile Fatal error from a MIDI backend. Errore fatale da un sistema MIDI di supporto. drumstick-2.9.0/utils/vpiano/preferences.cpp0000644000175000017500000000456514541630232020206 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "preferences.h" #include "vpianosettings.h" #include #include Preferences::Preferences(QWidget *parent) : QDialog(parent) { ui.setupUi( this ); connect(ui.buttonBox, &QDialogButtonBox::clicked, this, &Preferences::slotButtonClicked); } void Preferences::slotButtonClicked(QAbstractButton *button) { if (dynamic_cast(button) == ui.buttonBox->button(QDialogButtonBox::Apply)) { apply(); } } void Preferences::showEvent ( QShowEvent *event ) { if (event->type() == QEvent::Show) { ui.spinInChan->setValue( VPianoSettings::instance()->inChannel() ); ui.spinOutChan->setValue( VPianoSettings::instance()->outChannel() ); ui.spinVelocity->setValue( VPianoSettings::instance()->velocity() ); ui.spinBaseOctave->setValue( VPianoSettings::instance()->baseOctave() ); ui.spinNumKeys->setValue( VPianoSettings::instance()->numKeys() ); ui.comboNotes->setCurrentIndex( VPianoSettings::instance()->startingKey() ); } } void Preferences::apply() { VPianoSettings::instance()->setInChannel(ui.spinInChan->value()); VPianoSettings::instance()->setOutChannel(ui.spinOutChan->value()); VPianoSettings::instance()->setVelocity(ui.spinVelocity->value()); VPianoSettings::instance()->setBaseOctave(ui.spinBaseOctave->value()); VPianoSettings::instance()->setNumKeys(ui.spinNumKeys->value()); VPianoSettings::instance()->setStartingKey(ui.comboNotes->currentIndex()); VPianoSettings::instance()->SaveSettings(); } void Preferences::accept() { apply(); QDialog::accept(); } drumstick-2.9.0/utils/vpiano/preferences.ui0000644000175000017500000001601414541630232020031 0ustar pedropedro PreferencesClass 0 0 270 213 Preferences 127 100 15 0 0 Output Channel spinOutChan 0 9 0 0 Number of keys spinNumKeys 0 0 Velocity spinVelocity 0 0 Input Channel spinInChan QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok 0 0 Base octave spinBaseOctave 15 12 120 88 0 0 Starting Key comboNotes 5 12 C C# D D# E F F# G G# A A# B spinInChan spinOutChan spinVelocity spinBaseOctave spinNumKeys comboNotes buttonBox buttonBox accepted() PreferencesClass accept() 237 412 263 11 buttonBox rejected() PreferencesClass reject() 127 412 155 14 drumstick-2.9.0/utils/vpiano/vpiano.cpp0000644000175000017500000005424614541630232017202 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #if defined(Q_OS_MACOS) #include #endif #include "connections.h" #include "preferences.h" #include "vpiano.h" #include "vpianoabout.h" #include "vpianosettings.h" #include #include #include using namespace drumstick::rt; using namespace drumstick::widgets; VPiano::VPiano( QWidget * parent, Qt::WindowFlags flags) : QMainWindow(parent, flags), m_midiIn(nullptr), m_midiOut(nullptr) { ui.setupUi(this); connect(ui.pianokeybd, &PianoKeybd::noteOn, this, QOverload::of(&VPiano::slotNoteOn)); connect(ui.pianokeybd, &PianoKeybd::noteOff, this, QOverload::of(&VPiano::slotNoteOff)); connect(ui.pianokeybd, &PianoKeybd::signalName, this, &VPiano::slotNoteName); connect(ui.actionExit, &QAction::triggered, this, &VPiano::close); connect(ui.actionAbout, &QAction::triggered, this, &VPiano::slotAbout); connect(ui.actionAbout_Qt, &QAction::triggered, qApp, &QApplication::aboutQt); connect(ui.actionConnections, &QAction::triggered, this, &VPiano::slotConnections); connect(ui.actionPreferences, &QAction::triggered, this, &VPiano::slotPreferences); connect(ui.actionNames_Font, &QAction::triggered, this, &VPiano::slotChangeFont); connect(ui.actionInverted_Keys_Color, &QAction::triggered, this, &VPiano::slotInvertedColors); connect(ui.actionRaw_Computer_Keyboard, &QAction::triggered, this, &VPiano::slotRawKeyboard); connect(ui.actionComputer_Keyboard_Input, &QAction::triggered, this, &VPiano::slotKeyboardInput); connect(ui.actionMouse_Input, &QAction::triggered, this, &VPiano::slotMouseInput); connect(ui.actionTouch_Screen_Input, &QAction::triggered, this, &VPiano::slotTouchScreenInput); connect(ui.actionOctave_Subscript_Designation, &QAction::triggered, this, &VPiano::slotOctaveSubscript); QActionGroup* nameGroup = new QActionGroup(this); nameGroup->setExclusive(true); nameGroup->addAction(ui.actionStandard); nameGroup->addAction(ui.actionCustom_Sharps); nameGroup->addAction(ui.actionCustom_Flats); connect(ui.actionStandard, &QAction::triggered, this, &VPiano::slotStandardNames); connect(ui.actionCustom_Sharps, &QAction::triggered, this, [=]{ slotCustomNames(true); }); connect(ui.actionCustom_Flats, &QAction::triggered, this, [=]{ slotCustomNames(false); }); QActionGroup* nameVisibilityGroup = new QActionGroup(this); nameVisibilityGroup->setExclusive(true); nameVisibilityGroup->addAction(ui.actionNever); nameVisibilityGroup->addAction(ui.actionMinimal); nameVisibilityGroup->addAction(ui.actionWhen_Activated); nameVisibilityGroup->addAction(ui.actionAlways); connect(nameVisibilityGroup, &QActionGroup::triggered, this, &VPiano::slotNameVisibility); QActionGroup* blackKeysGroup = new QActionGroup(this); blackKeysGroup->setExclusive(true); blackKeysGroup->addAction(ui.actionFlats); blackKeysGroup->addAction(ui.actionSharps); blackKeysGroup->addAction(ui.actionNothing); connect(blackKeysGroup, &QActionGroup::triggered, this, &VPiano::slotNameVariant); QActionGroup* orientationGroup = new QActionGroup(this); orientationGroup->setExclusive(true); orientationGroup->addAction(ui.actionHorizontal); orientationGroup->addAction(ui.actionVertical); orientationGroup->addAction(ui.actionAutomatic); connect(orientationGroup, &QActionGroup::triggered, this, &VPiano::slotNameOrientation); QActionGroup* centralOctaveGroup = new QActionGroup(this); centralOctaveGroup->setExclusive(true); centralOctaveGroup->addAction(ui.actionNoOctaves); centralOctaveGroup->addAction(ui.actionC3); centralOctaveGroup->addAction(ui.actionC4); centralOctaveGroup->addAction(ui.actionC5); connect(centralOctaveGroup, &QActionGroup::triggered, this, &VPiano::slotCentralOctave); ui.statusBar->hide(); } VPiano::~VPiano() { m_midiIn->close(); m_midiOut->close(); } void VPiano::initialize() { readSettings(); BackendManager man; man.refresh(VPianoSettings::instance()->settingsMap()); m_inputs = man.availableInputs(); m_outputs = man.availableOutputs(); m_midiIn = man.findInput(VPianoSettings::instance()->lastInputBackend()); if (m_midiIn == nullptr) { qFatal("Unable to find a suitable input backend."); } m_midiOut = man.findOutput(VPianoSettings::instance()->lastOutputBackend()); if (m_midiOut == nullptr) { qFatal("Unable to find a suitable output backend. You may need to set the DRUMSTICKRT environment variable."); } drumstick::widgets::SettingsFactory settings; if (m_midiIn != nullptr) { connect(m_midiIn, &MIDIInput::midiNoteOn, this, QOverload::of(&VPiano::slotNoteOn), Qt::QueuedConnection); connect(m_midiIn, &MIDIInput::midiNoteOff, this, QOverload::of(&VPiano::slotNoteOff), Qt::QueuedConnection); if (m_midiIn != nullptr) { m_midiIn->initialize(settings.getQSettings()); MIDIConnection conn; auto conin = m_midiIn->connections(VPianoSettings::instance()->advanced()); auto lastIn = VPianoSettings::instance()->lastInputConnection(); auto itr = std::find_if(conin.constBegin(), conin.constEnd(), [lastIn](const MIDIConnection& s) { return s.first == lastIn; }); if(itr == conin.constEnd()) { if (!conin.isEmpty()) { conn = conin.first(); } } else { conn = (*itr); } m_midiIn->open(conn); auto metaObj = m_midiIn->metaObject(); if ((metaObj->indexOfProperty("status") != -1) && (metaObj->indexOfProperty("diagnostics") != -1)) { auto status = m_midiIn->property("status"); if (status.isValid() && !status.toBool()) { auto diagnostics = m_midiIn->property("diagnostics"); if (diagnostics.isValid()) { auto text = diagnostics.toStringList().join(QChar::LineFeed).trimmed(); qWarning() << "MIDI Input" << text; } } } } } if (m_midiOut != nullptr) { m_midiOut->initialize(settings.getQSettings()); MIDIConnection conn; auto connOut = m_midiOut->connections(VPianoSettings::instance()->advanced()); auto lastOut = VPianoSettings::instance()->lastOutputConnection(); auto itr = std::find_if(connOut.constBegin(), connOut.constEnd(), [lastOut](const MIDIConnection& s) { return s.first == lastOut; }); if(itr == connOut.constEnd()) { if (!connOut.isEmpty()) { conn = connOut.first(); } } else { conn = (*itr); } m_midiOut->open(conn); auto metaObj = m_midiOut->metaObject(); if ((metaObj->indexOfProperty("status") != -1) && (metaObj->indexOfProperty("diagnostics") != -1)) { auto status = m_midiOut->property("status"); if (status.isValid() && !status.toBool()) { auto diagnostics = m_midiOut->property("diagnostics"); if (diagnostics.isValid()) { auto text = diagnostics.toStringList().join(QChar::LineFeed).trimmed(); qWarning() << "MIDI Output" << text; } } } if (m_midiIn != nullptr) { m_midiIn->setMIDIThruDevice(m_midiOut); m_midiIn->enableMIDIThru(VPianoSettings::instance()->midiThru()); } } } void VPiano::showEvent(QShowEvent *event) { initialize(); event->accept(); } void VPiano::closeEvent(QCloseEvent *event) { writeSettings(); event->accept(); } void VPiano::slotNoteOn(const int midiNote, const int vel) { int chan = VPianoSettings::instance()->outChannel(); m_midiOut->sendNoteOn(chan, midiNote, vel); } void VPiano::slotNoteOff(const int midiNote, const int vel) { int chan = VPianoSettings::instance()->outChannel(); m_midiOut->sendNoteOff(chan, midiNote, vel); } void VPiano::slotNoteOn(const int chan, const int note, const int vel) { if (VPianoSettings::instance()->inChannel() == chan) { if (vel > 0) { ui.pianokeybd->showNoteOn(note, vel); } else { ui.pianokeybd->showNoteOff(note); } } } void VPiano::slotNoteOff(const int chan, const int note, const int vel) { Q_UNUSED(vel) if (VPianoSettings::instance()->inChannel() == chan) { ui.pianokeybd->showNoteOff(note); } } void VPiano::slotAbout() { About dlgAbout(this); dlgAbout.exec(); } void VPiano::slotConnections() { Connections dlgConnections(this); dlgConnections.setInputs(m_inputs); dlgConnections.setOutputs(m_outputs); dlgConnections.setInput(m_midiIn); dlgConnections.setOutput(m_midiOut); dlgConnections.refresh(); if (dlgConnections.exec() == QDialog::Accepted) { if (m_midiIn != nullptr) { m_midiIn->disconnect(); } if (m_midiOut != nullptr) { m_midiOut->disconnect(); } m_midiIn = dlgConnections.getInput(); m_midiOut = dlgConnections.getOutput(); if (m_midiIn != nullptr) { connect(m_midiIn, &MIDIInput::midiNoteOn, this, QOverload::of(&VPiano::slotNoteOn), Qt::QueuedConnection); connect(m_midiIn, &MIDIInput::midiNoteOff, this, QOverload::of(&VPiano::slotNoteOff), Qt::QueuedConnection); } } } void VPiano::slotPreferences() { Preferences dlgPreferences(this); if (dlgPreferences.exec() == QDialog::Accepted) { if (ui.pianokeybd->baseOctave() != VPianoSettings::instance()->baseOctave()) { ui.pianokeybd->setBaseOctave(VPianoSettings::instance()->baseOctave()); } if (ui.pianokeybd->numKeys() != VPianoSettings::instance()->numKeys() || ui.pianokeybd->startKey() != VPianoSettings::instance()->startingKey()) { ui.pianokeybd->setNumKeys(VPianoSettings::instance()->numKeys(), VPianoSettings::instance()->startingKey()); } ui.pianokeybd->setChannel(VPianoSettings::instance()->outChannel()); ui.pianokeybd->setVelocity(VPianoSettings::instance()->velocity()); } } void VPiano::writeSettings() { VPianoSettings::instance()->setGeometry(saveGeometry()); VPianoSettings::instance()->setState(saveState()); VPianoSettings::instance()->SaveSettings(); } void VPiano::readSettings() { VPianoSettings::instance()->ReadSettings(); restoreGeometry(VPianoSettings::instance()->geometry()); restoreState(VPianoSettings::instance()->state()); ui.pianokeybd->setFont(VPianoSettings::instance()->namesFont()); LabelNaming namingPolicy = VPianoSettings::instance()->namingPolicy(); switch(namingPolicy) { case StandardNames: ui.actionStandard->setChecked(true); ui.pianokeybd->useStandardNoteNames(); break; case CustomNamesWithSharps: ui.actionCustom_Sharps->setChecked(true); ui.pianokeybd->useCustomNoteNames(VPianoSettings::instance()->names_sharps()); break; case CustomNamesWithFlats: ui.actionCustom_Flats->setChecked(true); ui.pianokeybd->useCustomNoteNames(VPianoSettings::instance()->names_flats()); break; } LabelOrientation nOrientation = VPianoSettings::instance()->namesOrientation(); ui.pianokeybd->setLabelOrientation(nOrientation); switch(nOrientation) { case HorizontalOrientation: ui.actionHorizontal->setChecked(true); break; case VerticalOrientation: ui.actionVertical->setChecked(true); break; case AutomaticOrientation: ui.actionAutomatic->setChecked(true); break; default: break; } LabelAlteration alteration = VPianoSettings::instance()->alterations(); ui.pianokeybd->setLabelAlterations(alteration); switch(alteration) { case ShowSharps: ui.actionSharps->setChecked(true); break; case ShowFlats: ui.actionFlats->setChecked(true); break; case ShowNothing: ui.actionNothing->setChecked(true); break; default: break; } LabelVisibility visibility = VPianoSettings::instance()->namesVisibility(); ui.pianokeybd->setShowLabels(visibility); switch(visibility) { case ShowNever: ui.actionNever->setChecked(true); break; case ShowMinimum: ui.actionMinimal->setChecked(true); break; case ShowActivated: ui.actionWhen_Activated->setChecked(true); break; case ShowAlways: ui.actionAlways->setChecked(true); break; default: break; } LabelCentralOctave nOctave = VPianoSettings::instance()->namesOctave(); ui.pianokeybd->setLabelOctave(nOctave); switch(nOctave) { case OctaveNothing: ui.actionNoOctaves->setChecked(true); break; case OctaveC3: ui.actionC3->setChecked(true); break; case OctaveC4: ui.actionC4->setChecked(true); break; case OctaveC5: ui.actionC5->setChecked(true); break; default: break; } bool octaveSubscript = VPianoSettings::instance()->octaveSubscript(); ui.pianokeybd->setOctaveSubscript(octaveSubscript); ui.actionOctave_Subscript_Designation->setChecked(octaveSubscript); ui.statusBar->show(); ui.pianokeybd->setBaseOctave(VPianoSettings::instance()->baseOctave()); ui.pianokeybd->setNumKeys(VPianoSettings::instance()->numKeys(), VPianoSettings::instance()->startingKey()); /* * PianoKeybd::setKeyPressedColor(red) is equivalent to: * PianoPalette hpalette(PAL_SINGLE); * hpalette.setColor(0, Qt::red); * ui.pianokeybd->setHighlightPalette(hpalette); */ ui.pianokeybd->setKeyPressedColor(Qt::red); ui.pianokeybd->setVelocityTint(false); ui.actionInverted_Keys_Color->setChecked(VPianoSettings::instance()->invertedKeys()); slotInvertedColors(ui.actionInverted_Keys_Color->isChecked()); ui.actionRaw_Computer_Keyboard->setChecked(VPianoSettings::instance()->rawKeyboard()); slotRawKeyboard(ui.actionRaw_Computer_Keyboard->isChecked()); ui.actionComputer_Keyboard_Input->setChecked(VPianoSettings::instance()->keyboardInput()); slotKeyboardInput(ui.actionComputer_Keyboard_Input->isChecked()); ui.actionMouse_Input->setChecked(VPianoSettings::instance()->mouseInput()); slotMouseInput(ui.actionMouse_Input->isChecked()); ui.actionTouch_Screen_Input->setChecked(VPianoSettings::instance()->touchScreenInput()); slotTouchScreenInput(ui.actionTouch_Screen_Input->isChecked()); } void VPiano::setPortableConfig(const QString fileName) { if (fileName.isEmpty()) { QFileInfo appInfo(QCoreApplication::applicationFilePath()); #if defined(Q_OS_MACOS) CFURLRef url = static_cast(CFAutorelease(static_cast(CFBundleCopyBundleURL(CFBundleGetMainBundle())))); QString path = QUrl::fromCFURL(url).path() + "../"; QFileInfo cfgInfo(path, appInfo.baseName() + ".conf"); #else QFileInfo cfgInfo(appInfo.absoluteDir(), appInfo.baseName() + ".conf"); #endif drumstick::widgets::SettingsFactory::setFileName(cfgInfo.absoluteFilePath()); } else { drumstick::widgets::SettingsFactory::setFileName(fileName); } } void VPiano::useCustomNoteNames() { if (ui.pianokeybd->labelAlterations() == ShowFlats) { ui.pianokeybd->useCustomNoteNames(VPianoSettings::instance()->names_flats()); } else { ui.pianokeybd->useCustomNoteNames(VPianoSettings::instance()->names_sharps()); } } void VPiano::slotChangeFont() { bool ok; QFont font = QFontDialog::getFont(&ok, VPianoSettings::instance()->namesFont(), this, tr("Font to display note names"), QFontDialog::DontUseNativeDialog | QFontDialog::ScalableFonts); if (ok) { VPianoSettings::instance()->setNamesFont(font); ui.pianokeybd->setFont(font); } } void VPiano::slotNameOrientation(QAction* action) { if(action == ui.actionHorizontal) { VPianoSettings::instance()->setNamesOrientation(HorizontalOrientation); } else if(action == ui.actionVertical) { VPianoSettings::instance()->setNamesOrientation(VerticalOrientation); } else if(action == ui.actionAutomatic) { VPianoSettings::instance()->setNamesOrientation(AutomaticOrientation); } ui.pianokeybd->setLabelOrientation(VPianoSettings::instance()->namesOrientation()); } void VPiano::slotNameVisibility(QAction* action) { if(action == ui.actionNever) { VPianoSettings::instance()->setNamesVisibility(ShowNever); } else if(action == ui.actionMinimal) { VPianoSettings::instance()->setNamesVisibility(ShowMinimum); } else if(action == ui.actionWhen_Activated) { VPianoSettings::instance()->setNamesVisibility(ShowActivated); } else if(action == ui.actionAlways) { VPianoSettings::instance()->setNamesVisibility(ShowAlways); } ui.pianokeybd->setShowLabels(VPianoSettings::instance()->namesVisibility()); } void VPiano::slotNameVariant(QAction* action) { if(action == ui.actionSharps) { VPianoSettings::instance()->setNamesAlterations(ShowSharps); } else if(action == ui.actionFlats) { VPianoSettings::instance()->setNamesAlterations(ShowFlats); } else if(action == ui.actionNothing) { VPianoSettings::instance()->setNamesAlterations(ShowNothing); } ui.pianokeybd->setLabelAlterations(VPianoSettings::instance()->alterations()); } void VPiano::slotCentralOctave(QAction *action) { if (action == ui.actionNoOctaves) { VPianoSettings::instance()->setNamesOctave(OctaveNothing); } else if (action == ui.actionC3) { VPianoSettings::instance()->setNamesOctave(OctaveC3); } else if(action == ui.actionC4) { VPianoSettings::instance()->setNamesOctave(OctaveC4); } else if(action == ui.actionC5) { VPianoSettings::instance()->setNamesOctave(OctaveC5); } ui.pianokeybd->setLabelOctave(VPianoSettings::instance()->namesOctave()); } void VPiano::slotStandardNames() { VPianoSettings::instance()->setNamingPolicy(StandardNames); ui.pianokeybd->useStandardNoteNames(); ui.actionStandard->setChecked(true); } void VPiano::slotCustomNames(bool sharps) { bool ok; QString names; if ( sharps ) { names = VPianoSettings::instance()->names_sharps().join('\n'); } else { names = VPianoSettings::instance()->names_flats().join('\n'); } QString text = QInputDialog::getMultiLineText(this,tr("Custom Note Names"),tr("Names:"), names, &ok); if (ok && !text.isEmpty()) { QStringList customNames = text.split('\n'); if (sharps) { VPianoSettings::instance()->setNames_sharps(customNames); VPianoSettings::instance()->setNamingPolicy(CustomNamesWithSharps); } else { VPianoSettings::instance()->setNames_flats(customNames); VPianoSettings::instance()->setNamingPolicy(CustomNamesWithFlats); } ui.pianokeybd->useCustomNoteNames(customNames); } else { slotStandardNames(); } } void VPiano::slotNoteName(const QString& name) { if (name.isEmpty()) { ui.statusBar->clearMessage(); } else { ui.statusBar->showMessage(name); } } void VPiano::slotInvertedColors(bool checked) { PianoPalette bgpal(PAL_KEYS); PianoPalette fpal(PAL_FONT); if (checked) { bgpal.setColor(0, Qt::black); bgpal.setColor(1, Qt::white); fpal.setColor(0, Qt::white); fpal.setColor(1, Qt::black); fpal.setColor(2, Qt::white); fpal.setColor(3, Qt::white); } else { bgpal.setColor(0, QColor("ivory")); bgpal.setColor(1, QColor(0x40,0x10,0x10)); fpal.setColor(0, Qt::black); fpal.setColor(1, Qt::white); fpal.setColor(2, Qt::white); fpal.setColor(3, Qt::white); } ui.pianokeybd->setBackgroundPalette(bgpal); ui.pianokeybd->setForegroundPalette(fpal); VPianoSettings::instance()->setInvertedKeys(checked); } void VPiano::slotRawKeyboard(bool checked) { //qDebug() << Q_FUNC_INFO << checked; if (checked) { ui.pianokeybd->resetRawKeyboardMap(); } else { ui.pianokeybd->resetKeyboardMap(); } ui.pianokeybd->setRawKeyboardMode(checked); VPianoSettings::instance()->setRawKeyboard(checked); } void VPiano::slotKeyboardInput(bool checked) { //qDebug() << Q_FUNC_INFO << checked; ui.pianokeybd->setKeyboardEnabled(checked); VPianoSettings::instance()->setKeyboardInput(checked); } void VPiano::slotMouseInput(bool checked) { //qDebug() << Q_FUNC_INFO << checked; ui.pianokeybd->setMouseEnabled(checked); VPianoSettings::instance()->setMouseInput(checked); } void VPiano::slotTouchScreenInput(bool checked) { //qDebug() << Q_FUNC_INFO << checked; ui.pianokeybd->setTouchEnabled(checked); VPianoSettings::instance()->setTouchScreenInput(checked); } void VPiano::slotOctaveSubscript(bool checked) { ui.pianokeybd->setOctaveSubscript(checked); VPianoSettings::instance()->setOctaveSubscript(checked); } drumstick-2.9.0/utils/vpiano/vpiano.pro0000644000175000017500000000470314541630232017211 0ustar pedropedroTEMPLATE = app TARGET = drumstick-vpiano QT += gui widgets network CONFIG += qt c++11 thread exceptions CONFIG += lrelease DESTDIR = ../../build/bin LRELEASE_DIR=. include (../../global.pri) INCLUDEPATH += . ../../library/include # Input FORMS += \ vpiano.ui \ connections.ui \ vpianoabout.ui \ preferences.ui HEADERS += \ vpiano.h \ connections.h \ vpianoabout.h \ preferences.h \ vpianosettings.h SOURCES += \ vpiano.cpp \ connections.cpp \ vpianoabout.cpp \ preferences.cpp \ vpianomain.cpp \ vpianosettings.cpp TRANSLATIONS += \ drumstick-vpiano_en.ts \ drumstick-vpiano_es.ts # libs macx:!static { QMAKE_LFLAGS += -F$$OUT_PWD/../../build/lib -L$$OUT_PWD/../../build/lib LIBS += -framework drumstick-rt -framework drumstick-widgets LIBS += -framework CoreFoundation ICON = ../../icons/drumstick.icns QMAKE_TARGET_BUNDLE_PREFIX = net.sourceforge QMAKE_BUNDLE = drumstick-vpiano QMAKE_INFO_PLIST = ../Info.plist.app } else { LIBS += -L$$OUT_PWD/../../build/lib/ LIBS += -l$$drumstickLib(drumstick-rt) -l$$drumstickLib(drumstick-widgets) } static { CONFIG += link_prl DEFINES += DRUMSTICK_STATIC DEFINES += NET_BACKEND LIBS += -L$$OUT_PWD/../../build/lib/drumstick/ LIBS += -ldrumstick-rt-net-in \ -ldrumstick-rt-net-out packagesExist(fluidsynth) { DEFINES += FLUIDSYNTH_BACKEND LIBS += -ldrumstick-rt-fluidsynth macx { QMAKE_LFLAGS += -F/Library/Frameworks LIBS += -framework FluidSynth } else { CONFIG += link_pkgconfig PKGCONFIG += fluidsynth } } linux { DEFINES += LINUX_BACKEND LIBS += -ldrumstick-rt-alsa-in \ -ldrumstick-rt-alsa-out \ -ldrumstick-rt-eassynth \ -ldrumstick-alsa \ -lasound \ -lsonivox } unix:!macx { DEFINES += OSS_BACKEND LIBS += -ldrumstick-rt-oss-in \ -ldrumstick-rt-oss-out } macx { DEFINES += MAC_BACKEND LIBS += -ldrumstick-rt-mac-in \ -ldrumstick-rt-mac-out \ -ldrumstick-rt-macsynth \ -framework CoreMIDI \ -framework CoreFoundation } win32 { DEFINES += WIN_BACKEND LIBS += -ldrumstick-rt-win-in \ -ldrumstick-rt-win-out \ -lwinmm } } drumstick-2.9.0/utils/vpiano/vpiano.qrc0000644000175000017500000000020114541630232017163 0ustar pedropedro ../../icons/drumstick_32.png drumstick-2.9.0/utils/vpiano/vpiano.ui0000644000175000017500000002323714541630232017031 0ustar pedropedro VPiano 0 0 664 139 Drumstick Virtual Piano :/drumstick.png:/drumstick.png 0 0 664 23 File Edit Help View Show Note Names Black Keys Names Names Orientation Central Octave C Note Name Note Names true &Exit &Preferences &Connections About About Qt true true Never true When Activated true Always true true Sharps true false Flats true Nothing true true Horizontal true Vertical true Automatic Names Font true Minimal true C3 C3 C3 true true C4 true C5 true true Standard true Custom with Sharps true Custom with Flats true C true Inverted Keys Color true Raw Computer Keyboard true true Computer Keyboard Input true Mouse Input true Touch Screen Input true Octave Subscript Designation drumstick::widgets::PianoKeybd QGraphicsView
drumstick/pianokeybd.h
drumstick-2.9.0/utils/vpiano/vpianoabout.cpp0000644000175000017500000000240714541630232020225 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "vpianoabout.h" About::About(QWidget *parent) : QDialog(parent) { ui.setupUi(this); QString aboutText = ui.AboutTextView->toHtml(); QString strver(QT_STRINGIFY(VERSION)); #ifdef REVISION strver.append("
"); strver.append(tr("Revision")); strver.append(" "); strver.append(QT_STRINGIFY(REVISION)); #endif aboutText.replace("%VERSION%", strver); aboutText.replace("%QT_VERSION%", qVersion()); ui.AboutTextView->setHtml(aboutText); } drumstick-2.9.0/utils/vpiano/vpianoabout.h0000644000175000017500000000200414541630232017663 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef ABOUT_H #define ABOUT_H #include #include "ui_vpianoabout.h" class About : public QDialog { Q_OBJECT public: explicit About(QWidget *parent = nullptr); private: Ui::AboutClass ui; }; #endif // ABOUT_H drumstick-2.9.0/utils/vpiano/vpianoabout.ui0000644000175000017500000001371614541630232020065 0ustar pedropedro AboutClass 0 0 487 334 About false true <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:16pt; font-weight:600;">Drumstick Virtual Piano %VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:16pt; font-weight:600;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-weight:600;">Using Qt version: %QT_VERSION%</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Sample application for the </span><a href="http://drumstick.sourceforge.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">Drumstick MIDI Sequencer C++ library</span></a></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';">Copyright © 2006-2023, Pedro Lopez-Cabanillas &lt;</span><a href="mailto:plcl@users.sf.net"><span style=" font-family:'DejaVu Sans'; text-decoration: underline; color:#0057ae;">plcl@users.sf.net</span></a><span style=" font-family:'Sans Serif';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif';"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">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.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;"> </span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans Serif'; font-size:8pt;">You should have received a copy of the GNU General Public License along with this program. If not, see </span><a href="http://www.gnu.org/licenses/"><span style=" font-family:'Sans Serif'; text-decoration: underline; color:#0057ae;">http://www.gnu.org/licenses/</span></a><span style=" font-family:'Sans Serif'; font-size:8pt;">.</span></p></body></html> true QDialogButtonBox::Close buttonBox rejected() AboutClass close() 306 315 325 335 drumstick-2.9.0/utils/vpiano/vpianomain.cpp0000644000175000017500000001245114541630232020037 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include "vpiano.h" const char* PGM_DESCRIPTION = QT_TRANSLATE_NOOP("main", "Drumstick Simple Virtual Piano\n" "Copyright (C) 2006-2023 Pedro Lopez-Cabanillas\n" "This program comes with ABSOLUTELY NO WARRANTY; " "This is free software, and you are welcome to redistribute it " "under certain conditions; see the LICENSE file for details."); #if defined(LINUX_BACKEND) Q_IMPORT_PLUGIN(ALSAMIDIInput) Q_IMPORT_PLUGIN(ALSAMIDIOutput) Q_IMPORT_PLUGIN(SynthController) #endif #if defined(MAC_BACKEND) Q_IMPORT_PLUGIN(MacMIDIInput) Q_IMPORT_PLUGIN(MacMIDIOutput) Q_IMPORT_PLUGIN(MacSynthOutput) #endif #if defined(WIN_BACKEND) Q_IMPORT_PLUGIN(WinMIDIInput) Q_IMPORT_PLUGIN(WinMIDIOutput) #endif #if defined(NET_BACKEND) Q_IMPORT_PLUGIN(NetMIDIInput) Q_IMPORT_PLUGIN(NetMIDIOutput) #endif #if defined(DUMMY_BACKEND) Q_IMPORT_PLUGIN(DummyInput) Q_IMPORT_PLUGIN(DummyOutput) #endif #if defined(FLUIDSYNTH_BACKEND) Q_IMPORT_PLUGIN(FluidSynthOutput) #endif #if defined(OSS_BACKEND) Q_IMPORT_PLUGIN(OSSInput) Q_IMPORT_PLUGIN(OSSOutput) #endif int main(int argc, char *argv[]) { QApplication app(argc, argv); QCoreApplication::setOrganizationName("drumstick.sourceforge.net"); QCoreApplication::setOrganizationDomain("drumstick.sourceforge.net"); QCoreApplication::setApplicationName("drumstick-vpiano"); QCoreApplication::setApplicationVersion(QStringLiteral(QT_STRINGIFY(VERSION))); QCoreApplication::setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents, false); QCoreApplication::setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, false); QGuiApplication::setDesktopFileName("net.sourceforge.drumstick-vpiano"); QLocale locale; QTranslator qtTranslator; if ((locale.language() != QLocale::C) && (locale.language() != QLocale::English)) { QString qtTranslations = #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) QLibraryInfo::location(QLibraryInfo::TranslationsPath); #else QLibraryInfo::path(QLibraryInfo::TranslationsPath); #endif if (qtTranslator.load(locale, "qt", "_", qtTranslations)) { if (!QCoreApplication::installTranslator(&qtTranslator)) { qWarning() << "Failure installing qt translator" << locale; } } else { qWarning() << "Unable to load Qt translator:" << locale; } } #if defined(Q_OS_WIN32) QString dataDir = QApplication::applicationDirPath() + "/"; #elif defined(Q_OS_MAC) QString dataDir = QApplication::applicationDirPath() + "/../Resources/"; #else QString dataDir = QApplication::applicationDirPath() + "/../share/drumstick/"; #endif QTranslator libTranslator, appTranslator; if ((locale.language() != QLocale::C) && (locale.language() != QLocale::English)) { if (libTranslator.load(locale, "drumstick-widgets", "_", dataDir)) { if (!QCoreApplication::installTranslator(&libTranslator)) { qWarning() << "installing lib translator" << locale << "failed"; } } else { qWarning() << "Unable to load lib translator:" << locale << "from" << dataDir; } if (appTranslator.load(locale, "drumstick-vpiano", "_", dataDir)) { if (!QCoreApplication::installTranslator(&appTranslator)) { qWarning() << "installing app translator" << locale << "failed"; } } else { qWarning() << "Unable to load app translator:" << locale << "from" << dataDir; } } QCommandLineParser parser; parser.setApplicationDescription(QString("%1 v.%2\n\n%3").arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion(), QCoreApplication::translate("main", PGM_DESCRIPTION))); auto helpOption = parser.addHelpOption(); auto versionOption = parser.addVersionOption(); QCommandLineOption portableOption({"p", "portable"}, QCoreApplication::translate("main", "Portable settings mode")); parser.addOption(portableOption); parser.process(app); try { VPiano w; if (parser.isSet(portableOption)) { w.setPortableConfig(); } else { QSettings::setDefaultFormat(QSettings::NativeFormat); } w.show(); return app.exec(); } catch (...) { qWarning() << QCoreApplication::translate("main", "Fatal error from a MIDI backend."); } return EXIT_FAILURE; } drumstick-2.9.0/utils/vpiano/drumstick-vpiano.metainfo.xml0000644000175000017500000000242714541630232023016 0ustar pedropedro net.sourceforge.drumstick-vpiano drumstick-vpiano Simple Virtual MIDI Piano FSFAP GPL-3.0+

Simple multiplatform virtual MIDI piano based on the Drumstick::RT library, also using Drumstick::Widgets.

This program is an example of the Drumstick libraries. For a fully featured virtual MIDI piano keyboard see: vmpk.sourceforge.io

net.sourceforge.drumstick-vpiano.desktop https://drumstick.sourceforge.io/images/drumstick-vpiano.png
drumstick-2.9.0/utils/vpiano/CMakeLists.txt0000644000175000017500000001355614541630232017741 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set(CMAKE_INCLUDE_CURRENT_DIR TRUE) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Gui Widgets LinguistTools REQUIRED) set(EXTRA_LIBS) if(APPLE) find_library(CORELIBS CoreFoundation) set(EXTRA_LIBS ${CORELIBS}) endif() set(vpiano_SRCS connections.cpp connections.h connections.ui preferences.cpp preferences.h preferences.ui vpiano.cpp vpiano.h vpiano.ui vpiano.qrc vpianoabout.cpp vpianoabout.h vpianoabout.ui vpianomain.cpp vpianosettings.cpp vpianosettings.h ) if (WIN32) set(TARGET_DESCRIPTION "Drumstick Virtual Piano") set(TARGET_NAME drumstick-vpiano) set(TARGET_ORIGINAL_FILENAME drumstick-vpiano.exe) configure_file(${Drumstick_SOURCE_DIR}/versioninfo.rc.in versioninfo.rc @ONLY) list(APPEND vpiano_SRCS versioninfo.rc) endif() if (APPLE) set(ICON_FILE "${PROJECT_SOURCE_DIR}/icons/drumstick.icns") list(APPEND vpiano_SRCS ${ICON_FILE}) set_source_files_properties (${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) endif() add_executable(drumstick-vpiano ${vpiano_SRCS} ) target_link_libraries(drumstick-vpiano PRIVATE Drumstick::RT Drumstick::Widgets Qt${QT_VERSION_MAJOR}::Gui Qt${QT_VERSION_MAJOR}::Widgets ${EXTRA_LIBS} ) if (WIN32) set_target_properties( drumstick-vpiano PROPERTIES WIN32_EXECUTABLE TRUE ) endif() if (APPLE) set_target_properties(drumstick-vpiano PROPERTIES MACOSX_BUNDLE TRUE MACOSX_BUNDLE_INFO_STRING "Drumstick Virtual Piano" MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_LONG_VERSION_STRING ${PROJECT_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION} MACOSX_BUNDLE_ICON_FILE "drumstick.icns" MACOSX_BUNDLE_GUI_IDENTIFIER "net.sourceforge.drumstick-vpiano" MACOSX_BUNDLE_BUNDLE_NAME "drumstick-vpiano" MACOSX_BUNDLE_COPYRIGHT "© 2008-2023 Pedro López-Cabanillas" MACOSX_BUNDLE_INFO_PLIST "${PROJECT_SOURCE_DIR}/cmake_admin/CustomBundleInfo.plist.in" ) endif() set(TS_FILES drumstick-vpiano_en.ts drumstick-vpiano_es.ts drumstick-vpiano_it.ts ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_add_translation(QM_FILES ${TS_FILES}) else() qt_add_translation(QM_FILES ${TS_FILES}) endif() add_custom_target( update-vpiano-translations COMMAND Qt${QT_VERSION_MAJOR}::lupdate ${CMAKE_CURRENT_SOURCE_DIR} -ts ${TS_FILES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Updating vpiano translations" ) add_custom_target( drumstick-vpiano-translations ALL DEPENDS ${QM_FILES} ) if (UNIX) install( FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/drumstick ) endif () if(STATIC_DRUMSTICK) if (FALSE) target_compile_definitions(drumstick-vpiano PUBLIC DUMMY_BACKEND) target_link_libraries(drumstick-vpiano PRIVATE drumstick-rt-dummy-in drumstick-rt-dummy-out) endif() if(ALSA_FOUND) target_compile_definitions(drumstick-vpiano PUBLIC LINUX_BACKEND) target_link_libraries(drumstick-vpiano PRIVATE drumstick-rt-alsa-in drumstick-rt-alsa-out drumstick-rt-eassynth) endif() if(UNIX AND NOT APPLE) target_compile_definitions(drumstick-vpiano PUBLIC OSS_BACKEND) target_link_libraries(drumstick-vpiano PRIVATE drumstick-rt-oss-in drumstick-rt-oss-out) endif() if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") target_compile_definitions(drumstick-vpiano PUBLIC MAC_BACKEND) target_link_libraries(drumstick-vpiano PRIVATE drumstick-rt-mac-in drumstick-rt-mac-out drumstick-rt-macsynth "-framework CoreMIDI -framework CoreFoundation") endif() if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") target_compile_definitions(drumstick-vpiano PUBLIC WIN_BACKEND) target_link_libraries(drumstick-vpiano PRIVATE drumstick-rt-win-in drumstick-rt-win-out winmm) endif() find_package(Qt${QT_VERSION_MAJOR}Network) if(Qt${QT_VERSION_MAJOR}Network_FOUND) target_compile_definitions(drumstick-vpiano PUBLIC NET_BACKEND) target_link_libraries(drumstick-vpiano PRIVATE drumstick-rt-net-in drumstick-rt-net-out) endif() if(FLUIDSYNTH_FOUND) target_compile_definitions(drumstick-vpiano PUBLIC FLUIDSYNTH_BACKEND) target_link_libraries(drumstick-vpiano PRIVATE drumstick-rt-fluidsynth) endif() endif() install(TARGETS drumstick-vpiano RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} BUNDLE DESTINATION "Applications" ) install(FILES drumstick-vpiano.desktop DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications" RENAME net.sourceforge.drumstick-vpiano.desktop) install(FILES drumstick-vpiano.metainfo.xml DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo" RENAME net.sourceforge.drumstick-vpiano.metainfo.xml) drumstick-2.9.0/utils/vpiano/connections.h0000644000175000017500000000366114541630232017670 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef CONNECTIONS_H #define CONNECTIONS_H #include #include #include #include #include #include "ui_connections.h" class Connections : public QDialog { Q_OBJECT public: explicit Connections(QWidget *parent = nullptr); void setInput(drumstick::rt::MIDIInput *in); void setOutput(drumstick::rt::MIDIOutput *out); void setInputs(QList ins); void setOutputs(QList outs); drumstick::rt::MIDIInput *getInput(); drumstick::rt::MIDIOutput *getOutput(); public Q_SLOTS: void configureInputDriver(); void configureOutputDriver(); void clickedAdvanced(bool value); void refreshInputs(int index); void refreshOutputs(int index); void refresh(); void reopenDrivers(); void accept() override; void reject() override; private: bool m_settingsChanged; drumstick::rt::MIDIInput *m_midiIn, *m_savedIn; drumstick::rt::MIDIOutput *m_midiOut, *m_savedOut; drumstick::rt::MIDIConnection m_connIn, m_connOut; Ui::ConnectionsClass ui; }; #endif // CONNECTIONS_H drumstick-2.9.0/utils/vpiano/preferences.h0000644000175000017500000000233514541630232017644 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PREFERENCES_H #define PREFERENCES_H #include #include "ui_preferences.h" class Preferences : public QDialog { Q_OBJECT public: explicit Preferences(QWidget *parent = nullptr); void apply(); public Q_SLOTS: void slotButtonClicked(QAbstractButton *button); void accept() override; protected: void showEvent ( QShowEvent *event ) override; private: Ui::PreferencesClass ui; }; #endif // PREFERENCES_H drumstick-2.9.0/utils/vpiano/vpiano.h0000644000175000017500000000533114541630232016636 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023, Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef VPIANO_H #define VPIANO_H #include #include #include #include #include "ui_vpiano.h" class VPiano : public QMainWindow { Q_OBJECT public: VPiano( QWidget* parent = nullptr, Qt::WindowFlags flags = Qt::Window); virtual ~VPiano(); void showEvent(QShowEvent *event) override; void closeEvent(QCloseEvent *event) override; void setPortableConfig(const QString fileName = QString()); public Q_SLOTS: void readSettings(); void writeSettings(); void slotAbout(); void slotConnections(); void slotPreferences(); void slotNoteOn(const int midiNote, const int vel); void slotNoteOn(const int chan, const int note, const int vel); void slotNoteOff(const int midiNote, const int vel); void slotNoteOff(const int chan, const int note, const int vel); void slotChangeFont(); void slotNameOrientation(QAction* action); void slotNameVisibility(QAction* action); void slotNameVariant(QAction* action); void slotCentralOctave(QAction* action); void slotStandardNames(); void slotCustomNames(bool sharps); void slotNoteName(const QString& name); void slotInvertedColors(bool checked); void slotRawKeyboard(bool checked); void slotKeyboardInput(bool checked); void slotMouseInput(bool checked); void slotTouchScreenInput(bool checked); void slotOctaveSubscript(bool checked); private: void initialize(); void useCustomNoteNames(); QList m_inputs; QList m_outputs; drumstick::rt::MIDIInput * m_midiIn; drumstick::rt::MIDIOutput* m_midiOut; Ui::VPiano ui; // QStringList m_names_s{"do", "do♯", "re", "re♯", "mi", "fa", "fa♯", "sol", "sol♯", "la", "la♯", "si"}; // QStringList m_names_f{"do", "re♭", "re", "mi♭", "mi", "fa", "sol♭", "sol", "la♭", "la", "si♭", "si"}; }; #endif // VPIANO_H drumstick-2.9.0/utils/vpiano/vpianosettings.cpp0000644000175000017500000003363214541630232020757 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023 Pedro Lopez-Cabanillas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "vpianosettings.h" #include #include #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) #include #else #include #endif #include using namespace drumstick::rt; using namespace drumstick::widgets; VPianoSettings::VPianoSettings(QObject *parent) : QObject(parent) { ResetDefaults(); } VPianoSettings* VPianoSettings::instance() { static VPianoSettings inst; return &inst; } void VPianoSettings::ResetDefaults() { m_midiThru = false; m_advanced = false; m_inChannel = 0; m_outChannel = 0; m_velocity = 100; m_baseOctave = 1; m_numKeys = 88; m_startingKey = 9; m_defaultsMap = QVariantMap{ { BackendManager::QSTR_DRUMSTICKRT_PUBLICNAMEIN, QStringLiteral("Virtual Piano IN")}, { BackendManager::QSTR_DRUMSTICKRT_PUBLICNAMEOUT, QStringLiteral("Virtual Piano OUT")} }; Q_EMIT ValuesChanged(); } void VPianoSettings::ReadSettings() { SettingsFactory settings; internalRead(*settings.getQSettings()); } void VPianoSettings::SaveSettings() { SettingsFactory settings; internalSave(*settings.getQSettings()); } void VPianoSettings::ReadFromFile(const QString &filepath) { QSettings settings(filepath, QSettings::IniFormat); internalRead(settings); } void VPianoSettings::SaveToFile(const QString &filepath) { QSettings settings(filepath, QSettings::IniFormat); internalSave(settings); } void VPianoSettings::internalRead(QSettings &settings) { const QStringList STD_NAMES_S{"do", "do♯", "re", "re♯", "mi", "fa", "fa♯", "sol", "sol♯", "la", "la♯", "si"}; const QStringList STD_NAMES_F{"do", "re♭", "re", "mi♭", "mi", "fa", "sol♭", "sol", "la♭", "la", "si♭", "si"}; settings.beginGroup("Window"); m_geometry = settings.value("Geometry").toByteArray(); m_state = settings.value("State").toByteArray(); settings.endGroup(); settings.beginGroup(BackendManager::QSTR_DRUMSTICKRT_GROUP); QStringList keys = settings.allKeys(); for(auto it = m_defaultsMap.begin(); it != m_defaultsMap.end(); ++it) { if (!keys.contains(it.key())) { keys.append(it.key()); } } for (const QString &key : std::as_const(keys)) { QVariant defval = m_defaultsMap.contains(key) ? m_defaultsMap[key] : QString(); m_settingsMap.insert(key, settings.value(key, defval)); } settings.endGroup(); settings.beginGroup("Connections"); m_lastInputBackend = settings.value("inputBackend").toString(); m_lastOutputBackend = settings.value("outputBackend").toString(); m_lastInputConnection = settings.value("inputConnection").toString(); m_lastOutputConnection = settings.value("outputConnection").toString(); m_midiThru = settings.value("midiThru", false).toBool(); m_advanced = settings.value("advanced", false).toBool(); settings.endGroup(); bool touchInputEnabled = false; bool mouseInputEnabled = true; #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) const QList devs = QTouchDevice::devices(); for(const QTouchDevice *dev : devs) { if (dev->type() == QTouchDevice::TouchScreen) { /*qDebug() << "platform:" << qApp->platformName() << "desktop:" << qgetenv("XDG_SESSION_DESKTOP") << "touch device:" << dev;*/ touchInputEnabled = true; mouseInputEnabled = !dev->capabilities().testFlag(QTouchDevice::MouseEmulation); break; } } #else foreach(const QInputDevice *dev, QInputDevice::devices()) { if (dev->type() == QInputDevice::DeviceType::TouchScreen) { /*qDebug() << "platform:" << qApp->platformName() << "desktop:" << qgetenv("XDG_SESSION_DESKTOP") << "touch device:" << dev;*/ touchInputEnabled = true; mouseInputEnabled = !dev->capabilities().testFlag(QInputDevice::Capability::MouseEmulation); break; } } #endif settings.beginGroup("Preferences"); setInChannel(settings.value("inputChannel", 0).toInt()); setOutChannel(settings.value("outputChannel", 0).toInt()); setVelocity(settings.value("velocity", 100).toInt()); setBaseOctave(settings.value("baseOctave", 1).toInt()); setNumKeys(settings.value("numKeys", 88).toInt()); setStartingKey(settings.value("startingKey", 9).toInt()); setBaseOctave(settings.value("baseOctave", 1).toInt()); setNumKeys(settings.value("numKeys", 88).toInt()); setStartingKey(settings.value("startingKey", 9).toInt()); setRawKeyboard(settings.value("raw_keyboard", false).toBool()); setKeyboardInput(settings.value("keyboard_input", true).toBool()); setMouseInput(settings.value("mouse_input", mouseInputEnabled).toBool()); setTouchScreenInput(settings.value("touchscreen_input", touchInputEnabled).toBool()); setOctaveSubscript(settings.value("octave_subscript", true).toBool()); settings.endGroup(); settings.beginGroup("TextSettings"); QString defaultFont = QGuiApplication::font().family() + ",50"; QFont f; if (f.fromString(settings.value("namesFont", defaultFont).toString())) { setNamesFont(f); } setNamesOrientation(static_cast(settings.value("namesOrientation", HorizontalOrientation).toInt())); setNamesVisibility(static_cast(settings.value("namesVisibility", ShowNever).toInt())); setNamesAlterations(static_cast(settings.value("namesAlteration", ShowSharps).toInt())); setNamingPolicy(static_cast(settings.value("namingPolicy", StandardNames).toInt())); setNamesOctave(static_cast(settings.value("namesOctave", OctaveC4).toInt())); setNames_sharps(settings.value("names_sharps", STD_NAMES_S).toStringList()); setNames_flats(settings.value("names_flats", STD_NAMES_F).toStringList()); setInvertedKeys(settings.value("inverted_keys", false).toBool()); settings.endGroup(); Q_EMIT ValuesChanged(); } void VPianoSettings::internalSave(QSettings &settings) { settings.beginGroup("Window"); settings.setValue("Geometry", m_geometry); settings.setValue("State", m_state); settings.endGroup(); settings.beginGroup(BackendManager::QSTR_DRUMSTICKRT_GROUP); for (auto it = m_settingsMap.begin(); it != m_settingsMap.end(); ++it) { settings.setValue(it.key(), it.value()); } settings.endGroup(); settings.beginGroup("Connections"); settings.setValue("inputBackend", m_lastInputBackend); settings.setValue("outputBackend", m_lastOutputBackend); settings.setValue("inputConnection", m_lastInputConnection); settings.setValue("outputConnection", m_lastOutputConnection); settings.setValue("midiThru", m_midiThru); settings.setValue("advanced", m_advanced); settings.endGroup(); settings.beginGroup("Preferences"); settings.setValue("inputChannel", m_inChannel); settings.setValue("outputChannel", m_outChannel); settings.setValue("velocity", m_velocity); settings.setValue("baseOctave", m_baseOctave); settings.setValue("numKeys", m_numKeys); settings.setValue("startingKey", m_startingKey); settings.setValue("raw_keyboard", m_rawKeyboard); settings.setValue("keyboard_input", m_keyboardInput); settings.setValue("mouse_input", m_mouseInput); settings.setValue("touchscreen_input", m_touchScreenInput); settings.setValue("octave_subscript", m_octaveSubscript); settings.endGroup(); settings.beginGroup("TextSettings"); settings.setValue("namesFont", fontString(m_namesFont)); settings.setValue("namesOrientation", m_namesOrientation); settings.setValue("namesVisibility", m_namesVisibility); settings.setValue("namesAlteration", m_namesAlteration); settings.setValue("namingPolicy", m_namingPolicy); settings.setValue("namesOctave", m_namesOctave); settings.setValue("names_sharps", m_names_sharps); settings.setValue("names_flats", m_names_flats); settings.setValue("inverted_keys", m_invertedKeys); settings.endGroup(); settings.sync(); } QString VPianoSettings::fontString(const QFont &f) const { return QString("%1,%2").arg(f.family()).arg(f.pointSize()); } bool VPianoSettings::octaveSubscript() const { return m_octaveSubscript; } void VPianoSettings::setOctaveSubscript(bool newOctaveSubscript) { m_octaveSubscript = newOctaveSubscript; } bool VPianoSettings::touchScreenInput() const { return m_touchScreenInput; } void VPianoSettings::setTouchScreenInput(bool newTouchScreenInput) { m_touchScreenInput = newTouchScreenInput; } bool VPianoSettings::mouseInput() const { return m_mouseInput; } void VPianoSettings::setMouseInput(bool newMouseInput) { m_mouseInput = newMouseInput; } bool VPianoSettings::keyboardInput() const { return m_keyboardInput; } void VPianoSettings::setKeyboardInput(bool newKeyboardInput) { m_keyboardInput = newKeyboardInput; } bool VPianoSettings::rawKeyboard() const { return m_rawKeyboard; } void VPianoSettings::setRawKeyboard(bool newRawKeyboard) { m_rawKeyboard = newRawKeyboard; } bool VPianoSettings::invertedKeys() const { return m_invertedKeys; } void VPianoSettings::setInvertedKeys(bool newInvertedKeys) { m_invertedKeys = newInvertedKeys; } QStringList VPianoSettings::names_flats() const { return m_names_flats; } void VPianoSettings::setNames_flats(const QStringList &names_flats) { m_names_flats = names_flats; } QStringList VPianoSettings::names_sharps() const { return m_names_sharps; } void VPianoSettings::setNames_sharps(const QStringList &names_sharps) { m_names_sharps = names_sharps; } LabelNaming VPianoSettings::namingPolicy() const { return m_namingPolicy; } void VPianoSettings::setNamingPolicy(const LabelNaming namingPolicy) { m_namingPolicy = namingPolicy; } LabelCentralOctave VPianoSettings::namesOctave() const { return m_namesOctave; } void VPianoSettings::setNamesOctave(const LabelCentralOctave namesOctave) { m_namesOctave = namesOctave; } QFont VPianoSettings::namesFont() const { return m_namesFont; } void VPianoSettings::setNamesFont(const QFont &namesFont) { m_namesFont = namesFont; } LabelAlteration VPianoSettings::alterations() const { return m_namesAlteration; } void VPianoSettings::setNamesAlterations(const LabelAlteration alterations) { m_namesAlteration = alterations; } LabelVisibility VPianoSettings::namesVisibility() const { return m_namesVisibility; } void VPianoSettings::setNamesVisibility(const LabelVisibility namesVisibility) { m_namesVisibility = namesVisibility; } LabelOrientation VPianoSettings::namesOrientation() const { return m_namesOrientation; } void VPianoSettings::setNamesOrientation(const LabelOrientation namesOrientation) { m_namesOrientation = namesOrientation; } QVariantMap VPianoSettings::settingsMap() const { return m_settingsMap; } int VPianoSettings::startingKey() const { return m_startingKey; } void VPianoSettings::setStartingKey(int startingKey) { m_startingKey = startingKey; } int VPianoSettings::numKeys() const { return m_numKeys; } void VPianoSettings::setNumKeys(int numKeys) { m_numKeys = numKeys; } int VPianoSettings::baseOctave() const { return m_baseOctave; } void VPianoSettings::setBaseOctave(int baseOctave) { m_baseOctave = baseOctave; } int VPianoSettings::velocity() const { return m_velocity; } void VPianoSettings::setVelocity(int velocity) { m_velocity = velocity; } int VPianoSettings::outChannel() const { return m_outChannel; } void VPianoSettings::setOutChannel(int outChannel) { m_outChannel = outChannel; } int VPianoSettings::inChannel() const { return m_inChannel; } void VPianoSettings::setInChannel(int inChannel) { m_inChannel = inChannel; } bool VPianoSettings::advanced() const { return m_advanced; } void VPianoSettings::setAdvanced(bool advanced) { m_advanced = advanced; } bool VPianoSettings::midiThru() const { return m_midiThru; } void VPianoSettings::setMidiThru(bool midiThru) { m_midiThru = midiThru; } QString VPianoSettings::lastOutputConnection() const { return m_lastOutputConnection; } void VPianoSettings::setLastOutputConnection(const QString &lastOutputConnection) { m_lastOutputConnection = lastOutputConnection; } QString VPianoSettings::lastInputConnection() const { return m_lastInputConnection; } void VPianoSettings::setLastInputConnection(const QString &lastInputConnection) { m_lastInputConnection = lastInputConnection; } QString VPianoSettings::lastOutputBackend() const { return m_lastOutputBackend; } void VPianoSettings::setLastOutputBackend(const QString &lastOutputBackend) { m_lastOutputBackend = lastOutputBackend; } QString VPianoSettings::lastInputBackend() const { return m_lastInputBackend; } void VPianoSettings::setLastInputBackend(const QString &lastInputBackend) { m_lastInputBackend = lastInputBackend; } QByteArray VPianoSettings::state() const { return m_state; } void VPianoSettings::setState(const QByteArray &state) { m_state = state; } QByteArray VPianoSettings::geometry() const { return m_geometry; } void VPianoSettings::setGeometry(const QByteArray &geometry) { m_geometry = geometry; } drumstick-2.9.0/utils/vpiano/vpianosettings.h0000644000175000017500000001201714541630232020416 0ustar pedropedro/* Virtual Piano test using the MIDI Sequencer C++ library Copyright (C) 2006-2023 Pedro Lopez-Cabanillas This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef PORTABLESETTINGS_H #define PORTABLESETTINGS_H #include #include #include #include #include #include class VPianoSettings : public QObject { Q_OBJECT public: static VPianoSettings* instance(); QByteArray geometry() const; void setGeometry(const QByteArray &geometry); QByteArray state() const; void setState(const QByteArray &state); QString lastInputBackend() const; void setLastInputBackend(const QString &lastInputBackend); QString lastOutputBackend() const; void setLastOutputBackend(const QString &lastOutputBackend); QString lastInputConnection() const; void setLastInputConnection(const QString &lastInputConnection); QString lastOutputConnection() const; void setLastOutputConnection(const QString &lastOutputConnection); bool midiThru() const; void setMidiThru(bool midiThru); bool advanced() const; void setAdvanced(bool advanced); int inChannel() const; void setInChannel(int inChannel); int outChannel() const; void setOutChannel(int outChannel); int velocity() const; void setVelocity(int velocity); int baseOctave() const; void setBaseOctave(int baseOctave); int numKeys() const; void setNumKeys(int numKeys); int startingKey() const; void setStartingKey(int startingKey); QVariantMap settingsMap() const; drumstick::widgets::LabelOrientation namesOrientation() const; void setNamesOrientation(const drumstick::widgets::LabelOrientation namesOrientation); drumstick::widgets::LabelVisibility namesVisibility() const; void setNamesVisibility(const drumstick::widgets::LabelVisibility namesVisibility); drumstick::widgets::LabelAlteration alterations() const; void setNamesAlterations(const drumstick::widgets::LabelAlteration alterations); QFont namesFont() const; void setNamesFont(const QFont &namesFont); drumstick::widgets::LabelCentralOctave namesOctave() const; void setNamesOctave(const drumstick::widgets::LabelCentralOctave namesOctave); drumstick::widgets::LabelNaming namingPolicy() const; void setNamingPolicy(const drumstick::widgets::LabelNaming namingPolicy); QStringList names_sharps() const; void setNames_sharps(const QStringList &names_sharps); QStringList names_flats() const; void setNames_flats(const QStringList &names_flats); bool invertedKeys() const; void setInvertedKeys(bool newInvertedKeys); bool rawKeyboard() const; void setRawKeyboard(bool newRawKeyboard); bool keyboardInput() const; void setKeyboardInput(bool newKeyboardInput); bool mouseInput() const; void setMouseInput(bool newMouseInput); bool touchScreenInput() const; void setTouchScreenInput(bool newTouchScreenInput); bool octaveSubscript() const; void setOctaveSubscript(bool newOctaveSubscript); Q_SIGNALS: void ValuesChanged(); public Q_SLOTS: void ResetDefaults(); void ReadSettings(); void ReadFromFile(const QString &filepath); void SaveSettings(); void SaveToFile(const QString &filepath); private: explicit VPianoSettings(QObject *parent = nullptr); void internalRead(QSettings &settings); void internalSave(QSettings &settings); QString fontString(const QFont& f) const; QByteArray m_geometry; QByteArray m_state; QString m_lastInputBackend; QString m_lastOutputBackend; QString m_lastInputConnection; QString m_lastOutputConnection; bool m_midiThru; bool m_advanced; int m_inChannel; int m_outChannel; int m_velocity; int m_baseOctave; int m_numKeys; int m_startingKey; QVariantMap m_settingsMap; QVariantMap m_defaultsMap; drumstick::widgets::LabelVisibility m_namesVisibility; drumstick::widgets::LabelAlteration m_namesAlteration; drumstick::widgets::LabelCentralOctave m_namesOctave; drumstick::widgets::LabelOrientation m_namesOrientation; drumstick::widgets::LabelNaming m_namingPolicy; QStringList m_names_sharps; QStringList m_names_flats; QFont m_namesFont; bool m_invertedKeys; bool m_rawKeyboard; bool m_keyboardInput; bool m_mouseInput; bool m_touchScreenInput; bool m_octaveSubscript; }; #endif // PORTABLESETTINGS_H drumstick-2.9.0/utils/CMakeLists.txt0000644000175000017500000000243714541630232016441 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] if (BUILD_FILE) add_subdirectory(dumpsmf) add_subdirectory(dumprmi) add_subdirectory(dumpwrk) endif() if(BUILD_ALSA AND ALSA_FOUND) add_subdirectory(dumpmid) add_subdirectory(sysinfo) add_subdirectory(metronome) add_subdirectory(drumgrid) if(BUILD_FILE) add_subdirectory(playsmf) add_subdirectory(guiplayer) endif() endif() if (BUILD_RT AND BUILD_WIDGETS) add_subdirectory(vpiano) endif() drumstick-2.9.0/doc/0000755000175000017500000000000014541630232013300 5ustar pedropedrodrumstick-2.9.0/doc/drumstick-drumgrid.xml.in0000644000175000017500000002636114541630232020257 0ustar pedropedro ]> &product; Pedro Lopez-Cabanillas plcl@users.sf.net 2010-2023 Pedro Lopez-Cabanillas @RELEASE_DATE@ &product; 1 @PROJECT_VERSION@ drumstick User Commands &product; A Drumstick utility emulating a drum box. Synopsis &product; Standard options... Description This program is a Drumstick example and utility program. You can use it to create and play drum patterns. Arguments The following arguments are optional: client:port MIDI Destination Port. Prints a summary of the command-line options and exit. Prints the program version number and exit. style sets the application GUI style. Possible values are motif, windows, and platinum. If you compiled Qt with additional styles or have additional styles as plugins these will be available to the -style command line option stylesheet sets the application styleSheet. The value must be a path to a file that contains the Style Sheet. Note: Relative URLs in the Style Sheet file are relative to the Style Sheet file's path. session restores the application from an earlier session. prints debug message at the end about number of widgets left undestroyed and maximum number of widgets existed at the same time sets the application's layout direction to Qt::RightToLeft sets the backend to be used for on-screen widgets and QPixmaps. Available options are raster and opengl. display sets the X display (default is $DISPLAY). geometry sets the client geometry of the first window that is shown. font defines the application font. The font should be specified using an X logical font description. color sets the default background color and an application palette (light and dark shades are calculated). color sets the default foreground color. color sets the default button color. name sets the application name. title sets the application title. forces the application to use a TrueColor visual on an 8-bit display. count limits the number of colors allocated in the color cube on an 8-bit display, if the application is using the QApplication::ManyColor color specification. If count is 216 then a 6x6x6 color cube is used (i.e. 6 levels of red, 6 of green, and 6 of blue); for other values, a cube approximately proportional to a 2x3x1 cube is used. causes the application to install a private color map on an 8-bit display. sets the input method server (equivalent to setting the XMODIFIERS environment variable) defines how the input is inserted into the given widget, e.g., onTheSpot makes the input appear directly in the widget, while overTheSpot makes the input appear in a box floating over the widget and is not inserted until the editing is done. License Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation, considering as source code any files used for the production of this manpage. See also drumstick-guiplayer 1 , drumstick-vpiano 1 drumstick-2.9.0/doc/drumstick-dumpmid.xml.in0000644000175000017500000000763214541630232020101 0ustar pedropedro ]> &product; Pedro Lopez-Cabanillas plcl@users.sf.net 2010-2023 Pedro Lopez-Cabanillas @RELEASE_DATE@ &product; 1 @PROJECT_VERSION@ drumstick User Commands &product; A Drumstick command line utility for decoding MIDI events. Synopsis &product; options Description This program is a Drumstick example and utility program. You can use it to decode standard MIDI events as text. Arguments The following argument is mandatory: client:port An ALSA client:port specification that will be subscribed in order to read MIDI events from it. The client portion can be a number or a name, like in "20:0" or "Virtual Piano:0". The following arguments are optional: Prints a summary of the command-line options and exit. Prints the program version number and exit. License Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation, considering as source code any files used for the production of this manpage. See also drumstick-playsmf 1 , drumstick-vpiano 1 drumstick-2.9.0/doc/drumstick-dumprmi.xml.in0000644000175000017500000001007314541630232020110 0ustar pedropedro ]> &product; Pedro Lopez-Cabanillas plcl@users.sf.net 2010-2023 Pedro Lopez-Cabanillas @RELEASE_DATE@ &product; 1 @PROJECT_VERSION@ drumstick User Commands &product; A Drumstick command line utility for decoding RIFF MIDI files. Synopsis &product; options FILE Description This program is a Drumstick example and utility program. You can use it to decode a RIFF MIDI file as text, or to extract the embedded data to standard MIDI and DLS files. Arguments The following argument is required: The name of the input RMI. The following arguments are optional: Prints a summary of the command-line options and exit. Prints the program version number and exit. Extracts the embedded data to a separate Standard MIDI file, and to a DLS file. License Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation, considering as source code any files used for the production of this manpage. See also drumstick-dumpsmf 1 , drumstick-dumpwrk 1 drumstick-2.9.0/doc/drumstick-dumpsmf.xml.in0000644000175000017500000000751514541630232020115 0ustar pedropedro ]> &product; Pedro Lopez-Cabanillas plcl@users.sf.net 2010-2023 Pedro Lopez-Cabanillas @RELEASE_DATE@ &product; 1 @PROJECT_VERSION@ drumstick User Commands &product; A Drumstick command line utility for decoding standard MIDI files. Synopsis &product; options FILE Description This program is a Drumstick example and utility program. You can use it to decode a standard MIDI file as text. Arguments The following argument is required: The name of the input SMF. The following arguments are optional: Prints a summary of the command-line options and exit. Prints the program version number and exit. License Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation, considering as source code any files used for the production of this manpage. See also drumstick-playsmf 1 , drumstick-dumpwrk 1 , drumstick-dumpove 1 drumstick-2.9.0/doc/drumstick-dumpwrk.xml.in0000644000175000017500000000773114541630232020133 0ustar pedropedro ]> &product; Pedro Lopez-Cabanillas plcl@users.sf.net 2010-2023 Pedro Lopez-Cabanillas @RELEASE_DATE@ &product; 1 @PROJECT_VERSION@ drumstick User Commands &product; A Drumstick command line utility for decoding WRK (Cakewalk) files. Synopsis &product; options FILE Description This program is a Drumstick example and utility program. You can use it to decode as text your WRK files created with Cakewalk or Sonar. Arguments The following argument is required: The name of the input WRK file. The following arguments are optional: Prints a summary of the command-line options and exit. Prints the program version number and exit. Verbose output. License Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation, considering as source code any files used for the production of this manpage. See also drumstick-dumpsmf 1 , drumstick-dumpove 1 drumstick-2.9.0/doc/drumstick-guiplayer.xml.in0000644000175000017500000002705714541630232020446 0ustar pedropedro ]> &product; Pedro Lopez-Cabanillas plcl@users.sf.net 2010-2023 Pedro Lopez-Cabanillas @RELEASE_DATE@ &product; 1 @PROJECT_VERSION@ drumstick User Commands &product; A Drumstick GUI utility for playing MIDI files. Synopsis &product; options... FILE Description This program is a Drumstick example and utility program. You can use it to play Standard MIDI Files and Cakewalk WRK files with GUI controls. Arguments The following arguments are optional: The name of the input (mid/kar/wrk/rmi) file. client:port MIDI Destination Port. Prints a summary of the command-line options and exit. Prints the program version number and exit. style sets the application GUI style. Possible values are motif, windows, and platinum. If you compiled Qt with additional styles or have additional styles as plugins these will be available to the -style command line option stylesheet sets the application styleSheet. The value must be a path to a file that contains the Style Sheet. Note: Relative URLs in the Style Sheet file are relative to the Style Sheet file's path. session restores the application from an earlier session. prints debug message at the end about number of widgets left undestroyed and maximum number of widgets existed at the same time sets the application's layout direction to Qt::RightToLeft sets the backend to be used for on-screen widgets and QPixmaps. Available options are raster and opengl. display sets the X display (default is $DISPLAY). geometry sets the client geometry of the first window that is shown. font defines the application font. The font should be specified using an X logical font description. color sets the default background color and an application palette (light and dark shades are calculated). color sets the default foreground color. color sets the default button color. name sets the application name. title sets the application title. forces the application to use a TrueColor visual on an 8-bit display. count limits the number of colors allocated in the color cube on an 8-bit display, if the application is using the QApplication::ManyColor color specification. If count is 216 then a 6x6x6 color cube is used (i.e. 6 levels of red, 6 of green, and 6 of blue); for other values, a cube approximately proportional to a 2x3x1 cube is used. causes the application to install a private color map on an 8-bit display. sets the input method server (equivalent to setting the XMODIFIERS environment variable) defines how the input is inserted into the given widget, e.g., onTheSpot makes the input appear directly in the widget, while overTheSpot makes the input appear in a box floating over the widget and is not inserted until the editing is done. License Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation, considering as source code any files used for the production of this manpage. See also drumstick-drumgrid 1 , drumstick-vpiano 1 drumstick-2.9.0/doc/drumstick-metronome.xml.in0000644000175000017500000001030214541630232020433 0ustar pedropedro ]> &product; Pedro Lopez-Cabanillas plcl@users.sf.net 2010-2023 Pedro Lopez-Cabanillas @RELEASE_DATE@ &product; 1 @PROJECT_VERSION@ drumstick User Commands &product; A Drumstick command line utility for playing a metronome pattern. Synopsis &product; options Description This program is a Drumstick example and utility program. You can use it to play a MIDI metronome pattern. Arguments The following arguments are required: client:port An ALSA client:port specification that will be subscribed in order to send MIDI events to it. The client portion can be a number or a name, like in "20:0" or "KMidimon:0". The following arguments are optional: BPM Tempo in beats per minute. Default is 120 BPM. Prints a summary of the command-line options and exit. Prints the program version number and exit. License Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation, considering as source code any files used for the production of this manpage. See also drumstick-dumpmid 1 , drumstick-playsmf 1 drumstick-2.9.0/doc/drumstick-playsmf.xml.in0000644000175000017500000001024214541630232020104 0ustar pedropedro ]> &product; Pedro Lopez-Cabanillas plcl@users.sf.net 2010-2023 Pedro Lopez-Cabanillas @RELEASE_DATE@ &product; 1 @PROJECT_VERSION@ drumstick User Commands &product; A Drumstick command line utility for playing standard MIDI files. Synopsis &product; options FILE Description This program is a Drumstick example and utility program. You can use it to play standard MIDI files without GUI controls. Arguments The following arguments are required: client:port An ALSA client:port specification that will be subscribed in order to send MIDI events to it. The client portion can be a number or a name, like in "20:0" or "KMidimon:0". The name of the input SMF. The following arguments are optional: Prints a summary of the command-line options and exit. Prints the program version number and exit. License Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation, considering as source code any files used for the production of this manpage. See also drumstick-dumpmid 1 , drumstick-guiplayer 1 drumstick-2.9.0/doc/drumstick-sysinfo.xml.in0000644000175000017500000000570714541630232020135 0ustar pedropedro ]> &product; Pedro Lopez-Cabanillas plcl@users.sf.net 2010-2023 Pedro Lopez-Cabanillas @RELEASE_DATE@ &product; 1 @PROJECT_VERSION@ drumstick User Commands &product; A Drumstick command line utility to get information about the ALSA sequencer. Synopsis &product; options... Description This program is a Drumstick example and utility program. You can use it to build a report containing useful information about the ALSA sequencer. Arguments The following arguments are optional: Prints a summary of the command-line options and exit. Prints the program version number and exit. License Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation, considering as source code any files used for the production of this manpage. drumstick-2.9.0/doc/drumstick-vpiano.xml.in0000644000175000017500000002567714541630232017747 0ustar pedropedro ]> &product; Pedro Lopez-Cabanillas plcl@users.sf.net 2010-2023 Pedro Lopez-Cabanillas @RELEASE_DATE@ &product; 1 @PROJECT_VERSION@ drumstick User Commands &product; A Drumstick GUI virtual piano utility. Synopsis &product; options... Description This program is a Drumstick example and utility program. You can use it to play and watch a virtual piano. Arguments The following arguments are optional: Prints a summary of the command-line options and exit. Prints the program version number and exit. style sets the application GUI style. Possible values are motif, windows, and platinum. If you compiled Qt with additional styles or have additional styles as plugins these will be available to the -style command line option stylesheet sets the application styleSheet. The value must be a path to a file that contains the Style Sheet. Note: Relative URLs in the Style Sheet file are relative to the Style Sheet file's path. session restores the application from an earlier session. prints debug message at the end about number of widgets left undestroyed and maximum number of widgets existed at the same time sets the application's layout direction to Qt::RightToLeft sets the backend to be used for on-screen widgets and QPixmaps. Available options are raster and opengl. display sets the X display (default is $DISPLAY). geometry sets the client geometry of the first window that is shown. font defines the application font. The font should be specified using an X logical font description. color sets the default background color and an application palette (light and dark shades are calculated). color sets the default foreground color. color sets the default button color. name sets the application name. title sets the application title. forces the application to use a TrueColor visual on an 8-bit display. count limits the number of colors allocated in the color cube on an 8-bit display, if the application is using the QApplication::ManyColor color specification. If count is 216 then a 6x6x6 color cube is used (i.e. 6 levels of red, 6 of green, and 6 of blue); for other values, a cube approximately proportional to a 2x3x1 cube is used. causes the application to install a private color map on an 8-bit display. sets the input method server (equivalent to setting the XMODIFIERS environment variable) defines how the input is inserted into the given widget, e.g., onTheSpot makes the input appear directly in the widget, while overTheSpot makes the input appear in a box floating over the widget and is not inserted until the editing is done. License Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation, considering as source code any files used for the production of this manpage. See also drumstick-drumgrid 1 , drumstick-guiplayer 1 drumstick-2.9.0/doc/drumstick-ecosystem.png0000644000175000017500000042466114541630232020041 0ustar pedropedroPNG  IHDRN5#m pHYsodtEXtSoftwarewww.inkscape.org<tEXtTitleQt SVG DocumentwRtEXtCopyrightCC Attribution-ShareAlike http://creativecommons.org/licenses/by-sa/4.0/Tb IDATxwXSo{O Bqֽgkkj_jֺVܣQ@6H$Am+B!B!D ~MUm !B!(Ky^?! k7tNB!B|v"uB!B!+q"B!'"HD!B!D a$p"B!'"gH!B!BB!B/-ʼnR nBoxo(VG(£p<_QNB!B|Jݸq.N:E6A/~s;B?^B'B!B! _+T́;3/{a~{b=/&!B!C^~M*zup׭766f'S5dמ=mm:w⯓R6G*}mԮMjUD-(0y4ЪcJO?ŋuNJN&DGǼާNa˖Tݺv114 khKӪcGN1{޼_{~̪#B!"[կ74j$ZիsqƼ6z`X[YammУ;|=uk1/2zkWN=?l;7laCק7>e|OM&65.< &6+KKr~{g)tC `[oyg%twZ.E111yaJD{14< gYÏ3u ujբ* V;fTt4Cnjaɏ4u [Aݺh HD!B!DQES'cO%d&ޞHDDFŢǍG箄<@jj*2i 77Noo>̍[2 bΗ_zrPcLm׫{`)Mյ+ ?`؀ =okݻQ#YtJzry6lLIw^*FaQ2F=mڴiy] !B!W w|(Ww=M4͛cnfbnnF`@`llBj(h)[uv9XX$9:jafj @P͚'T&v7RRR(냫^}Z-O&fz]ݞRADFET&*a#&Ur-߯<[wښmR|y,-,hլC8ĩt,GF!B!Dtvfn:B/{QXۺWtʿ7[6 2-Q0#/HD!B!DQFq+닓#ukajbR`HD!B!D6RNu **d,:8B!BmPN$,!O'B!B!Z&kP v8B!BPsR$HD!B!D* &B!BIAXw8B!B1nVxmx %p"B!"Yut=NdN'!B!G̪$!rNB!Bd"2TGt8)^JD!B!DѤ~;4b (TF$!B!¶1_!+<*?3q6agk9?FD_ɧZtL ?|DB!BlPn_+F'S{\}y My,'lAL1z&qDѼտW/|˔c8B!BmL伆_3jZ4j oֽ٣Wc?X[P|ya7=N:@??,ueԨ9OKVuyGɴS+ 7Coz.<=)F;898bia?}ptpxɇ$p"B!S fV;[^8Orr2FF+;N%Gt`;!CJJ*,b6c@>4ϲEƺ|<{GO5!]͛psƭ[tmߞ/?!LF'[J_gдS G{i\dB!B!^R@oxҤc{oۊF۾d|6l\=^%KH]_W,ׯ]\8x?>z^Uf,[J-%5׮wn('bff9mGwdΝ|"`ٜtJbRǎa! _n"!B!BFt+ŁMPΏ~ÇQq#lܠ۾ivڷjMyPj=#'Npڵr(4[~~(4W7od6ݥ o#..]!s$''} ( %ۭ\ =|>g`؄X1iO.]wKVukٙ^غ{^-vPB -'[cFѫSg;wr\B!BQt|M~o₷'ػk7oӗAQ5` [K~oСUkB2|<=LL^79^ҥ~岥JQR%fΛǐ~P=S)S3l .{8y,VEasIbnf>b^%srzڴiB!B!ufFaUlmu']Qx*g)ڷÇ<~CCC3NmEiR+[uj "1+Z!JӋ&s)^@Yoo/Dİu.N[Z/ǑT+=NRɀ!B!"Z(5tշL^WE@;,ħiC7r B!xFr"BKS{&y] QGSRRܹ3C ᫯L:ǏpCCC ¦=g̘'~~~=WR,D,['h>}:-Zƍٳj2,YUV1i$>|8o6j~2ep6oܹs8qb_x6Y`oVߡ'97}+u6IsJBENrFʮ U"_P"l-*B|"߿?M4a„ ԩSǣVIMMՕKII!""Bܸqc9ž={ҥ Gё&MxL~#44=z`mmͰaGV… ,X W;TzèPBάal8os_-Q4?p_z8E7&r!I,NunaG#%5 cCSmiIJjwMpWn< Ց'ENDNH#!(#Yr%,> ζˉK$p"D;}-&<$ۭ"}+ggY@8ڸ`ㆣ;C>Z͊,c.zͥsWj00cܠZ~DZ7Pʹuu\)ϭKg'HCMq£ÈIFm"Csw[;`KR8[Mi\cKiP3@mʢ41ȇO'y@LB!+F\` h._s%}iS\=翱fVg_Mk%Y|4a@5Iߴ_f.61o҆tm1С{K#+|;׮ܢ}L1##C6I:*gִ]wjR0T閏^s qC]ܞ@QqqeL8XaoUL>7D= S6PatOWO'B!ȗcC100$8h`^W%ݸw+wOVN;OlZkʥ33vV|{z-ɶKJ+EbV'>8|]Q1YgZEE~$!˩ (T&"cu^j]Tƒ<FWcll4I33Su.3fػ0A]*큝ޱ\[=#+~]G@.BOJJ*ׯަ^}ND <.xFx] ֛f8> T򮋣Mq<JT)]?WO((l?5o3z6+IYTGBO'y@˯%9)u| EA=&>Œs:8@"@Oh;?Oϫeڪ(i^B|\( i۹3d\%xsX7L:/%]|x1~_5wiZѵO["c8~ o6߶i^R #95 /?7tShSWS㓷"Hwi:Z[}PmFlQн1,O^\'# Zm@P@LLK !DQ4{/jKf7HNJ&QKqvuaөߤŒjeY|%JŅWHLxetFFtтG* ח?BsϮDD=M޼W]i:M[P&Fؗcp*w,"Hྊ 0Zzj1P ω|6ahM>޻{A*W#dQ._΄C M!D;3DV{{^ !Da߇d$X ֬؊9=C(hRcZ[7u-6nG܎o9W,W'i#rQa08w(WEſVd'(ZʺPBI[ LXP}+fY [֒d Jҹk堆zlZFCԨz؉忬AZl=CAaܔA3hL/t;ϭfBBH8zq:{RVϮA-Ezs?.6^w?6&+Kvݶ5oEkXmf\z;˲4gۜ;umBp׮§SТmB_)H;K^N޶Wt\83!鷞'eŭhө{4~^9oxU[9HNҦ+#-Z+_|C˟ңpjTB_=N~_g&&:oegc3w%<\$..wPB,YҦc&ANM021B0;|2mvl ٞz;NBWPu/>([~zO}TM2e鶝yVL )싑qu]X&"8I%ha,C'Xl;+oZ)Y(YR(xٞھ$%н_[([bdlHz;tي+ق%]f{p P015ܔ۬4փɟ Tzv6ԨI}B";T:"*ZZ j#ݺsZa&MQm2ӵ^औ?ow_Vv^wQDHD"8)EX?Ę fDR3wwo:AczO~Doٞ ~t>;^tQQP tgim>ŤDQ"WJj2n|:.O}{u( c$ SLMV8ٸ`㊽^p/z5Bd(25E;pBn^D=!}I[fiF=yEQ03Kf]n9,, +Q899III`hX#lj_9(-g£;O!wӆܣi*m368ǣ*֮xN<!]Ec)8r_[IAi߭M,DAL\\ܻwWWWf͚ETTs… tؑ+Wp%:v숣# TXEG.] 11+xbRSSر#wΎ0N>͓'O055Ą-Qt^s!rƍ׸z:S/2:-0%ˑԽϟؘ2e9=ۙ_z5[f@ZYf͛jԫW+WիWt钮Z槟~"$$?`oߎ[^ȂR"99݅ >Ѧ8&Ff/,_&[l:G'w}˿sNZhm{^hh(ȱT9=ߊv̪B0g&>>-Zйsgv؁Z~>ڵE$$$0rH|}fڱcL4 $N8ApvviӦ?z :GtR>3:tݻ177ًޡaZK K,--(X:IDD%JT^VVV111X[[G(QaPT$&&(xj$$$@JJ јcdERh}6ŋXo{\\fqww}*22+++P;w`hh ۠x♆޾}kkkTi3#Iz_iS'EfVgBb׮]s1puuͲܓ'Ot8<-EߟSUTjժ)S###vƍ1c3gd֭ҠA-ZDyʵ Ewh}y9)_'Ndݺux{{sE֯_?֭cє,Y7o2rHƏOf͚4nܘ'NLbb"Ύ;;;*UG}ıctQ#00nݺq}022bժUXXXPZ5;5jΝ;>|8'N8QFpeΝKniÚƌþ}(V/^d˖-+Wuƅ prrחYSLܹsұcGIHHחKVU5㤤HHHܹsVZaiiIrr29 !4r0&ղe˨_> ,bŊL8sbccÕ+WtN8GcǎTZSRreEK.c``@۶miӦ :u CCCfϞܹ͛3_}MHx'"ƳKQ޽ Zx뭷ػw/~~~<~ *иqc*W m۶\ݲeK ŋ)W'O$..F1i$ٲe >&L`ɒ%ݛ={0x`-[E?k׮¹s_>[Ғ3gO4ŋ3c ̙ZY4={6Geݺu駟W_}7l4=@cjjʡCfʕ 4iӦѨQ#͛(t1BDzR'E(lj" D~%Ir\t9sߢhhٲ%|cƌARN .]UVL: r-|2#FҒ3fPreHHHϏCV7nmڴɫf(4 u5r\xtӠ_LAN,y4tn *rR;~~iӬY3o߮ <ϏА޽{tROΒ%Kx7000`Ϟ=̜9SWgϞ4lZjѷo_Tرc ̲+W /HLL ˗/֭[_{aРAYMV\9w&&&@Zŋ =y.p68r|󍮍[nͺu^1ᡡ$%&u5DE;pRTfՁ6BQ.]ҥ>erYBBBpvvf֬Yd޽3a;,YsαchӦ 5j`ܸq\zʕ+cnnuеkܼdT {_Mxx795d.~61 ͫ-RÅmҥw+ Ǿ,,3V{>mp&l/N` g!RhNRS54]l`w_2}Fz"k}nΜ9Ջs2}tvMBBB1 WT7ߤuL6˗~ -ɳ722Ғ$s<|"$%%qiڶmɓiڴ)qqqi][nC@Lq-[+ 99Y% ܬi@@K,aРA_]r%?~=Zw=gggfϞ^^^Bzػw/ժO>W_}iѢ5B }y] ѣGܺu ~ZjXh.OHH۷twyKKKZjEJ(UUTAf9leʕ+Ǒ#GteN:/ UX䰒_WBW5ݲCCz՞vᜱ?tŰ`ڽXX<vDIWJ٫T };Pj9>ޓZѤUf{s|ϧ-AZ~ag066bظjUkU[ 3+Q{PO۱ܹuU}yt?[2iuԔT~ [8 >]f|.Qn?L~+%%&cFقɅzpBN>1cƌw}`Μ9Cǎu=B~Gʔ)[0`.oӇҥK̙3ܹZʹiӰÇ,\P+m۶8 bժU~ zFF& J>I_tǿvDZlk vYI2痵s061Ĉo~Z"})n0,M?on}ZoE7^9˗|תUKo9=‚&Md:%ڵ[WX1+.ϖ)Ydʕ\rg206h@wLY/k777:w[NO+yE:pBq NDjsO6Scs6LMy]-!DW9Dv6$&$mHͺ3?V뚴Ppt6mZWJC=]^/іV`dlt)tν[`kgE9CC5֍,83s]tj5;6^cddH\\%]YU,BQd'*CnݻĽ[.MEAMEm$)LN9~n8:fc su zA;-:k=}֧$mCT&}B1qvuf*;IMIe}Ԭ['תk~t)VQ4֋}024~B W㗅 ܾJAͭKBZ:w+c6JN6 U٣*Μg{ 0o6k l]>酏IcDEP9A$6}0O lͲW)ӄ2Pe*;Vo}dDtAYgʼy(Tܑ^ڳin>ZAczd'l,\cti:/ȸ^^ϜdzTq+'[e4}He_B^*⩻gϣh[;"{~h 8ZO\=Vja6zY*XGݚЛxwtm6M~uO˱ 1߰֙҆U;)ō+8|ɋRg;o07u#Eb`shiЬ&.)P (4T^?`enǰCS`fbJ: Gk6=p`in^;$w'I,+;7U3x8p-Ą'}Je2hy`jZ 0PЦKc<+(1 񉤦jP?kQ2C;ahUJ-3MOqT]Mpߔ(銭5py|1gJz]ޛ9}>Czi%,, VK%HLLڵkp=?իW:-_Γ'OسgFFFԫWO6))}D }"r-6j""99^t'steѸr-SVr;7.wmˆ>S .;{#gQQ -;4$*21o~{3ٔoM<#Oу|{3y{pekoabbL_&}23Ef N|4V.3y -@5m ãp+įbLӂ?x*ӿsW=שusk hWo/YFK\\zs?{wa_t8{Q}w%"MEy` Ն%$ =^;KrPS[[8RxEP+S@VXg]%i]f IDATxێhSIJI`pۙzuѤH5&h4A~_+c,lƀYe$7IOYu*Wuf*g6Ak1Tg7)k^$:>+glh^ 074#ᦱ)j#mrGJlfb*VAm356g`uMtmy`ʷX=EWž9CR03e5G^Zβ~iprp}=N?awnƕPlYc4Jk_n߽ V-{yfWNГw1*N OW g=|L@!{Ob:na1ӵp:?{KG`ÊbW]c7EF%֨? Xb{`ņTDEE@:?OOPQg73oݷoG<} MCR2 ӃȎ+00ҧWZujec x3l\/T[zy0a\6[>$ꝞH߿v,_XQesy&ڵ͛ҳgO篿bQ~}j֬Ill,G&00TTBŊ177gΜ9۷7n`kk.7oLV=)Av\ 7{).5WS|r=yKA&Faׂ$%&sYbb4L]$Ą$8M' k̙^mFQҌ``dc2~\VmEjJ*ݛG9/O#8LLT IJLb~(tHĽ\>]Ǽi+Yd#'å*]j€_x:6[BGbcHIIE$xGR{];/p/یoP 4SJ?ZZrjԭOSW@Gߺn7)R-gi3$ Yw*t|j&_7 !/\MB[>'r/ Iq(0/!'xR Чv90ۘHHA.~/gy?~ept{C_ϳri-fPuTi={r)c}m,z~9>(bWJv?i&^KM I xKN.e]kiNŸfwEּ)u4třyd =]P`mi,UfFV#{H_3KQJʷ *›+?hʨ㝩@r}3hɵ)6w!5 0_)BGutqvulhikiKߖcˎ p(dceίs/ZvsҨ;4M?g$Ibo9~,Qϣ)l_'~٥ $ae[/S*14ԧj,2`hͮVܿN24lQ Ihձ*KZFtޒaTa=\$Q#FtQZ]\#p-Ngxڳ؀G`h΍WNӓD۶mR2I庼rfBҥc`}hSOY6vX;V!.YgM߅Zi.ǠܲnG޴M:| KkL5勻6wdxo3˦?q7<*[NWu2LCe\)Qj=XL͌ػykhrty>zGC69spI___7+ 4Zjaddڵk9vAӽ{wڴiC͚5:t(mڴAKKCCC._V]oܸAbӧ 6ݝ~ѿhѢ/_fرбcG9ŖR " I!y_doJJ ::5-vs^D\0R}&ļDBԐ~ОjP۪ ,}EB"^Qzw~\e]b[z.5u2tBs 6#}zp[.fkFVl0A>f…4nܘ2eЯ_?u%.\P߿_999BA:u(V*OZjӻwo |"EXh+Vܿ#GvZټy366^n(B$rѳ #6EnM4Ws Ol^qgrtUL8GG +yx?R9L5jnc _ߛҺ L z IHEOǀ~ͧ*_K'>0 KPeqwwtd2ڶm˜9sS2 /BΝ_3W^Ϗ6mPL aӥKJ.ӧOmۆׯts%>a8rRWZ}@Rb2w9s"knç7[zk9# Zy.J/FJJ*s' !>3!Gs8=qj^!!1R U=ۏNZZSIuLBqv)0 uP}e`DDehѾ>wng 9V-̤̉gHtqM3WFM>|+려F&qi*U/5ϟ`,)Uލ`eS*iahMؼf=Kp)q)[gªhwi yu`CK病n5jƆ7np-lllŋ3.])޽{x'''LMUKN/D=(#kOeL2y z{~i{=YARb2/b137F[7miIy ˄h]aeVZZ }카$Yr3WCӪ0NmHLtJnjdoU62\9%fV䞌}͹߶y;C*ͱが8L;ws#uZoǭK?WȳԳ'3U.]Uklj 00G H^eyz:$Ym4U`irк@jƟ'/Tv^C, >x)U]v GGG>h)V,sق> 044@M84 _{GexD|Ƴ>&*1YߞB[3#k̍YW=G.P/w~*z?W3{?H8>7vl7<Q|qtp`t,]±@ițo`Eԯ]W]UvvYcof@~3fgu {)ӧs+8LGjj*FdiX[Y)#Ǎ a\c4_^]}Raݗ}&$Š%-5d/Zs[uk3G6ʭmBsJ5| iٲ%Vݙ;w.jen8}4?#ǎENXls*H(DRg<2S#KL,){ }=‹H!*1 } ?/՝haabfFXq5$Av#ImQa'$'pxlWЈJ۠{ qeS̟͠=G#0ҮwCCq&K'IhFй}_ܼ}ჿd +KKO$I4mCTTge5 ?~{|Lƍص/wCC155<99ƭ[L9s2{V-]J%8|[ׯg)[ iْϞ?i6?N5ΗP%NΆ$>5G(qT\/"n`n, '|F(ZЃ1VpAWRJi8CbK1痖\ [,Ll2k^UJ6yLϢ#}'=Ӑ{ybbn`17X Y9@jr ©|9,o)EAlɩ\?r U.ϏGoXr ýDI [hxh|=BAa'gVo5E 0&JЩm;xAܼ}*+аn=@1W7͛p+Z7ӳswޓ4HPI IthCvmYd)# atݛG X1\ zzvrYco6sa ;93tH<1a޲0Uݛ_Ј d 2JLƍ Ύo4SLKڵk  мysټy3gϞEKK=z3{ATTcƌ!>>Ill,6mCWW>}!$&&|r.\%Æ KUprr^zdzh"]FҥmM'4;$ޝO 4,:;z޽PN>ͿѦEKM]PR%J0{|Ι\zfyZ ݫWDftԙ8Ѳi3LWmƍELL ƍcO?cgm7y&&:zuQݳ֮ckzdr#j5oOkr)r56mbGJJJ:^zS*WLZɉWd23֭[8{,Ǐ'5U#::ݻGB2^ fذaL8Q&$$ϟSre\Rx% V700 )) R)V .5Z-.Ղ/M.=nj g,TN8Te J͢BBb?y6jk)07F4E-4:YLAq}4Q1 %_CԨIm@֪HXѠN@؈i&`iQ?ʚ@hǍc0שˊ`dlHTXBܮ=Nv?gEWW'9 044§E '>>|}2r$wݥL)wƎcЬ7Fޮ{C$}Ү];/_N $  )HȈÇSvmjժE*UƆgfؿvƗ h*a%\E[E~ IDATɻכN!oTSƾfϣmํ2um0FH$$2[З{2kS?|T^@yyJc|Y'TXqq.̬)?v ?P8{iv6,0} 00C>q:g =wd8>̜<5_,T=O?a߹6pb ,|5s2\.GTǢE2r X[[ӠA-[jii!I֭K 1˗/aÆxbn/&!!eb`` ߿?=kkk-ر#Νrd2~' DRRAAAL)IHH{aÆlٲKA>8_ \Kf[VO2}Ve`7%*65JdZdlEY8Xfǐ csm3#߿p= ׍1N^sqѣ4c6 "Erf$_NTKu[, -PN+! %KdРAxyyaff̙3@__RJe0=z !!֯_ ;v,&LgϞ92å5%Kcǎԯ_ڵkGϞ=Xt)ÇG&1p@ZZZ޽QF1o}űS95 l-)R#U48zaJcHN"B.hUs`NŹv{ҢzߜVEz|‚,b߄FT/}³l-?\ؓg6iE63%$du=h &%aPjtK$dzc uaVGI˧7sQj<~~?̓ C=ӏѳ7N6Kwz4_ IDYu|U'A!?xCk@ HC S<.Si9c m],Җm#zî@at^X}$=S]RJ(x:be#~|KE-{3R |dNi58/>_FD_ye W^e籏y8+z:GK?b_M *o<6yQBض%"qY`"_隸}5ׯ@r+iѢE{y)Zh6=zf͚ex\[K=SYG}~aOns=7{KZchO\<HK.\vM-syzEJJ ]tax{{SF VZngƌO4h@]?!!jժѹsgBCC3k֬Zj4oޜӧO?~:PJ:uĽ{4YfTT)Ci۶->4lؐU2w\uR3fczꅧ'ŋv֮]K:uSnY$)z?'wѾ0}5bnh&'9E5VQk|qʪKȮ#AWMA^si ضm7oFOOOcNDD$?>LJJ Jf͚ѣG,X={_~$%%ѩS'9B…9{,VVV,X+W2gaoo$Ilܸ}}}VXAxx8 4ˋڵkSZ5j֬Inu~76mڄ/1-[oDD7od9sMru,--122bȑ/^|}}Y~=7nܠm۶\rjțm6i҄3gbooϕ+Wӧvvv`nnN޽z*+WdӦM888зo_Ν˔)S8|0ܹKKK ߬& Iqq68*R2gT)|i58yL /A5> L+ >}ʈ#tޝ3gP(fǎ 2зo_j֬ؼy3ǏW I*TqqqAGG }4h@Fѣ;wPWѬY3*W޽{ڵ+ʕ#11P\]]9rZ6mڨ,XP^\\M6e„ 4iRԭ[?޽{еkW*T@ǎپ};[no߾ FK.~;;;d2c J,InՕRJg(VE%99;;;u[:::akkP(K===(Jʙ3gP>znϟ}=ŋju}1˗W{5GA9+wNae@j]{ɴqБO;7!$ׯo֨1ePݻ{ngLA\ )dJ]]6̺6x$))aDFOeYagswo '+[l v]XD˵>\!dYf[@ vA˝:::$fݻRSSձMmll矙8q"5bk׎~zhS&St4ؼ/^ wܡArA^'ӦMS֭[7tPj׮M8s vvv8::ǒ%K2qifys>'Qa&1Lܝ=27?r--MOB?{oq/B_7^`/[J4'&&|R{Vx.Qu ǹG( fͦ Yf8y.iYH5haoer$%ZlzI+B+EBD Aҥ9s ;wΎӧgh(VΌ;]rA˗PZ5RRRHNN@jՊǓŋ3# #Gd9sk׮ѴiSy9'ODKK tԉʕ+b ʔ)Cpp0: ^:su mmΘP+﨏RLY*)Ne\1Z[k!;cԬWGEt4 }^}$㉎IW/w=Nrq/&׍Z-ւzzzx...[ 2djI>}A&k.O΄ h޼9˗/BAXX~~~0f֭ ԯ_8bAraq1~xƌLcnnNҍȈYfQpa~G166\r̘1sbkkի H":tEcu|5j 7?9sOOO=/ݻqppPzSE~BÆ*GʕYv-k֬A[[ٳgvOZs)YzS}^q#~˭3g{>X\8r7WWa.$)bbc8r$݋e)]M=]:P' d7ߨg|KZؗx͎}{Q*tʖeyL'J%y6:07T*173y[-zTs&DlvYҺր8B_A!24ӪY;;REsZ Kvw;=d'''SlY~WWrsmɍ[-U4EaCXRr2.ٻy+5<=Yq#?濚➕9(n.*ɜ_rp2p]~ށǎѾwOWdFUxS=]]u~8uVfbM.{"55,*T[sZAƢ+;u Cc¨hki1|XʉY;_nϦ-dn K$IQDV |HK.dŊt 777a4R*IlQ?Sֶz2w2jyiYp{f JVEDq._I"**}ѭ]{u}[;z::t!tL?n=]]?]-n;p~(/ccs;|QMp{@ xwOi5r=LEF Q"& /~N#%%5$%) .T$)Q*SHIIM;D))Tl(Rc0> :vH˖-sZВ~ }YEt4/ӥ-RA: ÿcXlܶsfq v߀ ka͔+UwIJ@Ԩ_I#XF0wakc٩ HHLǝIMA"GUW#Β{B{QI"4JghHI0&`$"e_)6xWΓ+yj2<&ɨSfiN ٳarn0l/e4}llZ3w0gu/@!(JVm?KV[Kx1]۶R0e50Sݩ3SaH<<4Rz ڊ¯8yE>R N^ލEYX 'No[i3{VVKĢ->w3HNHdY< OIr*eQ*S_KS{9¹߶MjtWYÇ5t(NQ. % ~[ 3*"WR@wq7mj L&íJ%۾w*&XtPSw4NLkG.עc\ Rw%$'2B 2(Y]c?ݟޝ;cd`|U% * !clj9JF&B"Lg}K' *DҖ>\AQuQ ؎;^''Qf6~ rfC.|Gǜy6ٟ~cwsq|:ݛߎDBU觓9t7_FQ\[ں9 #)2g9Tgdkx}l7;WǏ\΃qÆa-j>$q(AOY%iU47v͛@M; D;{ /p;Qe@StHS(fJ3x3P۵PѽFf_jQKkxuq-VϹm?PǑ[-Vwb51 :"ӫ>u[wg>eJ*B(ݨf9%%)Sx}cV's:r9vv7WW! 'La9B8npoR{1&@ |,Q2m&JadRN$]b_R;ei=;v.\¦h;v4NҪ@6MqUӤJ/jiMؓ[h"XqƮDV㋒*|Q>,$1Ny+A[innU*xڼz ==<+yY7QY@ 68++Z~? {W$v0/?~B\܌v42U=?/T?jQ֝M"íHߌ*i;xn#!WCEA>ARJH<_TDɗE É Oy?gΥX*To5-"rQdqZZXZu$d2&L!ٳR_ \Erb"[FR*i;އhr/179Q}+IFr OU:}6!.~T_2:ŝ ;Suk0gѳ{_XKA~@))Q*~y$J+AeT[~Mr*펑O]{I&P;2@ J$?#*1&b`jj2<-N 'R=?z2*z[G{ϓ iC}~i#Sy ,J%25 IZ%40r5)IIl1m]]0-B}L&QjrǼxI! LM:^L&Qo(]6U=8$uĘ֘ IDAT+؏0>I<_T%D$~^HԣǴa:5="|ЫAaR$1=CC:LIJ&$'A Hzu)t0oNwPg~νW103ͻhѮPN]œdr*od FYܹs{#_ťKx ?3cǎeٲe@ ᷃vvX>&j`ng]HO&{ޱʃ`ڽ)7|N8'O4h}Edd$+WLJSRP!BBB8pk׮LJ_KLdd$ UV :-ZOhh(~~~.\֭[ 0GrިmKurA)*D>wk׻nU*}PDjD=QȝLdfMKըp*ΦIӸwj ~;;Ww/T$M D&mLƠPʫFk(x-eBXd30B#nh?~%Kl2Zh%Kptt̰=W[nϏ*U}vnܸ/(^8...ԬY===^xiӈˁLJ>}||R25559 /JNMDxsQI#*6m:Nt,HP`^BW[֦̽aT,?@ Is罆A&s 5%+WY#}NeTe&3c7\x2e?{Tq{EK'PY *䧠(@"*)l(ݻ?ZEinjs~r$%%1d,X`xLֺ|ٳ&ر#۷ogʔ)<,Y7777nLjjaoooϟٱc?8{졠8,Yn"dMR8fD_UHXV}5=ygo=4jƦ^c88_:QBarNp!egn7J{&<oC4~777/^c=f޼y4nܘ?}a 7@.]8z(ڵٳgŷG:t^Ohh(#Gȑ#s=pBNʠA:tQUDuV( t3^}x?4ڙ4ovG=.}@Qse{%^F%( !˓r[%%u0imZuu詃'lXæ˦iӦ1n8&L@TT>(nnn>tue'lfq|G!hl nC4Y8V!]G zӹ'\ƚzz2haEΥp",FD.8\}-_2Į^6!=/)!5R5 ݌{LLdgMQՏQ}_w]Ԏ#*NչIjIDX ;Nی3(m ! p*%8>ަ-'"~0ݍ[JDCS+9 )2DXG?8?t1J;B!LI%I$h4jG& '?v羥Fpv\-MTAUUTT gX'DXt읜U;Ba2SR^DD0oٺ%?m D6wpcW;6:8QۭDX'wkf VE'‚dgfoUUחHB!,EEigeHg %le)߂4 Oұy]߱jǷø[MG_ovw.}h?lpbKDX}eF}7/y/.{!dDF\}aONLu{z0Ԏ#̞"#?IDX<`6#c(+meBSa@ۛbG5%=)Y '&GZ}n:m{$RZHD#A@xp"ªr eF$& ,|AԎ%́MkZuSa"ēk1BD$Qp6ǎEԃ3O θ[s0gK_l~ф ?sr"aN'9JB!/1.^ɗf%q;^91'p47>+KՎ%L",F^SuX?o!{hՕd}!BmSطaƌB1 QZTnq4hh״Q=8};# S& #‰0oBvuJ;^{@UB!ԖԝI{Վ %Ů-޴F#a &arO02ֶB)5R;`yEgYi&wgpׇiQHBm2DK;&%[aֲ3t6Q;Ba2SR T;`u,O S>DʉԎ%"R8f-;=F٩CQ rrT B[ Ĵ 'gs2c-^;fbjPG"TaNgolq [CP(B!dS;J-Ej6x7R;mvrMԎ#TP^Vƙrqa9g)3wP^Zv B[d8W !FQV\LUO8{, dw)i;{[GR+Ws((Ԍ=Bk}q 2@8B!O(cԎ"`YLJ6 8wĖp`co'#N4 qJ‰Ba"XTakov:6Old7Vl?aͮAѠQ;dD0>VR#DZ})/)Q;Bac[SUY}E C>kcФQ_.Ȇ~V;HDe|/|v XEȞ}jGB!LstsQhrNfEw}WMf7Gap"VvZ:>&4ᛃ3'DhjGB!_ײ~Bc\ѝoNr 5Qc|MK1Mqmp"RYq Y&wЖ-s.BX))6T;ir+.(*'#o&}}<# !̄&,@Q#Ԏ"BXETLp"[s4;/ogŶٔWּyB#a2pC(u*+.V;B`N=FEiYNϜj&Bѵ@^mbՎofڱ&N ',egv:f.?LX!LI \ѽ􏩬P;0!v6k7ot_8B'abt~jǨSPt8HiQQBN`TF(WGey9W;0AxnWTr/S !L7"ՎpYah4dNԎ#B\w}Ǝ8@x黓 kva ((udWwy@pc !TQEQ;f7pcEJ}%gԎ!DpsYB\.{jGWP\GYizr+t{лpܝՎ%DpF z%2DǶ%KAQ 1H>1hN~<=`186٧S;CQYMi .^vN]ux/2xDHOՎaҤp"~ӭUNrymY;[3variޑ1}Ԏ"uu$Zv aD%;;ԎRo6ŅT)"J+!<]}ՎS^_Ź=L+mmNNEnjǸfR41W a\>p"'(+?Bf7I&aid.eŜ"IQIDtl T;J髪Ұ:)"Hݙv !Fyysg-'}2ZLyyf.">R;&cϿy io$y>֮cjF$HDɤ2DdxR(S;P"Kegoփb`g_ҒR***|nUUyۊmW{6EOYq1ijGuRZ Sң1*++)-) З^vbL=r߿VVLdBQIH}٪EԨ*ʸz}AGYQ۵E5B](/0dTQ*** qj˱Vio+`аL2;nzKby4VNN` BY^cG3r{?Re0 mk2'" 7w prvD.&#|ǯ|D8nvׇG?L{%%Fm,+/5z_iohv߭'a}'n_rCwfS\TƵH=埒r0^Ww3O3eƫЍv͔?%*:coϾgw*6'^OqQ .Ϊm+!3i!.F!Վqllm[FnQ63V?DiE>{7MOW?FVcoݔĄS~Ln ~2!W|/v/~kb{'SfyUB3S<[,_~%ѦCk hٟSYg a$oגiϳn՟'paҒz~I<ɟ?6zMt/sgs~f}-!A<2+>lN˳{oi0C:tb3 ȏuakgѻ_um[=;;zGJm>N&Kϰwo #UHD| varKNQZQֲ;S9شWht7XI~5qyV -@ϟ `xyֵse/8~4dz1Rv$thڇEӣXR3[3NfԠqY!/>>y_L 9٧('^X_v^:AthO( n8i7,YEE%cG=Xn gȖ HO=ơ8{~ᅰo>!weh۔ ^>HT0ztN`Ɯh4o4+` aA‰n}"psR;F+=uh⏃w ?;:9p*v`R5hwJQzFn}<>>vLP[6dpNJIEgco+3'w MԎoAAv 8qtٞnϨnhrtrp;~!=#MshG0~_OܝzGa2YngoGߛoWć@~^!?z_~?1|-'zEQظv;K_W?LAOaGZ/R8f쉓f?ӓԝIR81 Eo#23 ̘ ]wqx6>]ɁfOf|u*>?rX)Giy;IϾO GGޜ<,Z߰f*35(:M'"> hdK)`u3\rk 'ɤ)\ޖ ;yqqqO2su¹\ޙ kUw}ڎড M?ά#7'^]2IDx򒒫h"R rrpIԦW,4a^jVǸG Q}.}EE%3(t>:Pba鏿Ko>0hpswa_!J,@DJv-;{[n@F/TzGLs[ ѐ5&ٕ &M}5)iEG(`$5WX֜n8歛 /(MG O|?MBGM֮9,^9Q7w׃fxzQlݰ P ⵩v Í 4ѐdևjӽ' IDATNٰs4K5n {GGv%;Q8VOo\k^_0`p/4-7*@1#[/~&Сkᶇ;a Ռ>j vvf$:ts;?<HK-:Y _:vkc]8iX u 4<1\E77tvmz\ܜiӱE{HD#tjAZn)=^x;T7|sy|Sd{] V|EǒÇT e$P[̸Ssٗ|OFT$۽>p"L^lqpqV;&߿ Nvf#N .jL- F'ȶ,"!]WQ_qL }|YpX#&Ny @_dHG '䭟|n{ygm(+.R '*‰^FԛS\) Zt."<5b<9 mقOI:g1h %^>)NmՎq]xz~jXUuW1INy]ˢUUVy7R;J'$+Z#+rT'Ւ}>p"LZeEgsuXd9UG_8BUEMdTkc-W9[J?))vQUUEXEa>d߲d)8vU^RJo]?p tO !L '¤ee쌇-~="ۻW 81 "eLI%0*R g^Ubw:v(2~RX1G '¤egdJ5,ƆwJDEP8aqȾe2ҴSc4V-IKJI-J+\A>˄}>p"LZB㺋c_RYQqT(> ! ^O`dQ\D|,ɫ I_!a|AD$RQVα} kv-ןV1L+&{tݷ,; ijhٽa[dvr1\aqn~ggN厍8IXܜ<|.κU[>yo6J&'~}Xo ¬FAjh0~ mv [6jh4tF.4D>q@`kkK@/=uf}ptrhGQa1.Ni3~-Mb®&mS2wwAZ7b/y8u/Ix (E*)(ʈ/[M1/Oc(6?GvMESQYG?&+7jmVPCCʆQVZNp#qcï{syK'}{!}0f3<} mwo/Yo|0+-koc1W8G¼HD&E!;ͺvV;0.#9v0]RR\J#̞+?E36_d8lшL&IU RG2/v4䀂q#(/h˔;uLI 2\$B Bz*Q)<xmgN#$,EeeǏdǵ~=]׋MC{ҳcw?}fo^ kFY5 FlWwg NDWlO?[N##5M%NI=uB#Ԏ"ՊnNĶ+ʰSp2{wف`V*5훒qqsY(>tqD#*+-89;ҳg<<ؿ't #lݸR"i%[.'; ZNC8'2zo*nδܺobBjƲkMV(wJBVFDƄŊJ>w gW'o섻k=2QVVw'Z}1yb$6'gNH :tcdg>;DZ#'ɉ ٰz;tG).*%,m =tvkWnFn=B񩨰u+pL.^t!O/w%bPZRFd4mIiIE%x^v(7%( ZEѺM:MTVU$$hӳ_g$c[_y@̠֮ˢՔFn^Px**,V纝˧GߎuNQtX6[7:[Haa1- dgatIlk86<˝eFmPm9?gGZE/ndba3tZ,^G(OJk TϏx}<]|gN( ZœsMVsP߰hZ_l#oG|ÄAOe}:f[x 3^RYQ(y7ź[رyzy_LҶ}8o~\ſs9ݯr9E1C_`GfN_ϿKpdfL̃o1uLETY^xpAmeY?1룅]w֞mS2gLڟ~p-,)EE*+*h̃إvٿT.g (B‡L~&<5?^gMGX6*ѥyez?煫>6vş̙#Gr0EQ8~$F0%I:C).*aƔ9ϿXwMEQ&߷r̘2ۍX:^~b%(8% W7k JQ76188e.E b#pvq2_TXLtQ]ذffhsCk?Oߟ˖I}S$_+׆5H_m,ǿςYt#=cGzF֮L6wrzEˏzCGYw\EQxx+Ȳ2r89-2~dĉ0I3nQF(/C۶J8B\UTUIO9f&j?2 45:in9z(e[ޖl]X~v#Ko?jx*=:q/˖~ד dgAoƾ$<Z潅_7P'sߠ?()))#/psܗb |=>j" f-zjPF[llm kLZR2L a^=\!}2)kvIޱ+?}Xi9CF(MӂUp?kWnǃ9L3~9ݓ֩:;/^OE^6ת_60i4a -b̤}WOy***C86av|9^›O*ZE›էr,51-#kmWaoV$FCFG.NT*ԢRw^WWJ 5kMCx?_nW7'@ݙ@z 茝 /wNtl2 :*/ܷf& M$ ȧt6Z>Vma/vQvĴ0?;^g!'eo]'e-{x$tlax,iczd׮:_޹G6T͆lubUlٰ-prdUsa[zsC62gs@wdIL( /ܱ/r4q:;ungGqSٲaWq52Dݺh"ija=qo fN]S]]izFǂ\}]|fyxԺpp侪*= S#S2eL~p(^% ՓuNlSg^ovlCph ή5q80%Βs&Z›W_]BW/Od:n>jGQEDB-x7R;1_]s4$A>ǜvO\y~ߴߴoƶk΂1/܍Wy F]U9ӧrliy=]MĶG<~miO]{\N>{wb$UyTWSq@Sf̳j}>V>Fx?kuZOn}AzscތI 'B, "gRw&IĢtK4\wȗ5)hί{1M>wuϯJ^|ߠ{3^m/=1"&Nw9?_Qj39u=( L } O^f=.^rpQc~^?5}kv AR8QC]}ٸ4)/kb[C @Ѡ\߬YZm*գLyYZ7e֢wH9xIs'Y4 BټUUz>=߮'88gc$&_/YF|:HEʼn]Zvf>˓ݟpEg#zA^!9xx\XwO7P=\PX/7léՎ3/p_~= ~ySuPFCD k01fˏc?hLI9QkWn{mUGᆾrǍdG+u%PTTRgȗĊK ؗF3ۻwܹm?wݎ3عuou~pA!,5iIr<Ƶ;yw4-"/ޜ<~b}~Pkݺ;o*@dP͢ #?Jf׮o޺ /M~ '{^|lS9K>l}4$i).o,FQ}g35V Mi%m y31,a7?I˻0Mǖ~tny݀'dOk RNv TV|۾)GM̩sd#/ j}_^mqvqddqtMl#$ Ou͌E_ѱl:Pҳ-ޘő4iF!=jӾk,CFȨ[ߠlt`jc`ˇLdP6~=+;_%??-4W9Ox`mLQ:6:x??߶exnN1i^|l Igs8vEKbL+> އELNjG1B[P;h@2V`ogGwQߋ-#iш͹s ^xaZ%43ZGV4@zLqq)q@s !aDńFh.̣A+y׮94mBIq)xxkRg{K7ʩ$CK<< $M vq]v n޾8L TOE#=v$,2{;tZ-`:'[vtusYHBk"m3úz jh޺ w{ ήddMbՎ"sY|r#tp9^RR^h MGDL ˓- ;ٴ1ZGzCNKYY9z'(ğV 1prv$y8MC squiB#.VBLHCARPPN{0 ggGr h*}=m;oB&M$,66E2>FRTXBxz{ФY6[z|iшvBӕ@aQ1aD7>jD!=(--CQ{b8qxayLHnݞ"llt<=ޟv03`h"eeGNi&`rΜQ7',*¼BJKh&>7gy6nMGŤilO"G+*/(,ŕ6M{ECKZVE\od'tWINI.r _z}߮vեHfu4G49UGrOa qfEfqY>#Yڮ&߿X̔T"#hRChoJ2en5j53i1jۍ|#MaabeպgP( FmƎOU/Wy8&QW_ fes/1+(P8FzWl>Xw\c6Cмw_/e/$L%0O&H騾p"LJvz.^8{x\}e cHٱK ' ny,ϡG+jlՎ#,qY }UpQLZd+VԊ;ݫvbl+J1K =3t;&ۡ^p"LJiaC M!Nc^vȾe9N=Fey9jG1 ֭HOJM7AQ'}| %~HDn#nS;"uv(zŲ/?)$p1x\}e+NJ}Ney96v7gVׇN0!ahd$!sGX4Go.&}*=<4c'<.V8ƥ(OE}B+‰&fMIݕ$(d*Ⱦ%,( IDATJ‰89| !.O 'd=~ʊ Ԏݻw1,Ս8Bt> {'˞LnV2DB a2v,['Ubw#X=U*c4գkP4ٵ,BfJ*=^y;㘔 jGPb$5}Zɾ_?R8&#;-jVBQ,j290 ٷ,9%Eq\|| !.O '$(z=MN5oJPHcXɩ:H_))pI*W;I(叹 Hu'njDZHP89?_x90 C-ːJԎa}e|gZS_,KO~aұ+(P(&ё7l",ͻuQ;e\tEEC}s.3@qrYlv~\XX9E '$d71ZN(&ޞ-kN^a/8oDÑ=ejG1Y.5!mn+*X'Ɣ~RX+G '$tu=ՎaR"(z=V8GQM?!enrjUD$đk7T;Q(V02(:)ꊣ],mg}]=a_tX^¯`VQd;рd2w!civ ǖ n>>j1 K'KJ?))a< $ugN5M h8o kܬ)Nޖ?JZ##N‰P]UePZv&48UAEC}KXN #GH(a-( h@>˄}>UM/ؾIjG19ovU=IJ'SrBq42K!Վb QFX(hH?)")ev aeT9NDC]ˬ޶C[KDI)(']^p"TN!Ԏ!΃5iP{%Z,-/mEC}˜,UUUq#͟˳O>@p۶mvmt֍QFj7dv]wU뾞={r)׬e#hٲ%͚5c믿Ҿ}{5kSO=Eyy9˗/SNDGGȾ}Lڟib)U8 0s3SՎ"-EYdiE4C1vX5jǏ';;5kZ履~۷{1}t+t3[ф/gbѢE̜9A1vX;F[n瞣nsΩfΝ I``  <<<ؼy3FZسg&Lۛ9s_RZ5֭3&M⫯[ncmm/^߿LNiҳѧfKӓMvjTX׳|^[#n-EᑐtBA 'm۶U/^Ldٲe2yd\]])]4˖-cyzE *%>9,i$+N>-t٩dN~\Iב{Na\}Su$K$J zm6v?>,5potOkbرcXXXΦM4hzm۶ 4u￳uVN?=z ""J*ѧOvɘ1c=z4W^̙3\x޽{뉎fڴi^;r]h޼DtL0]vLF011!==SSS:ẁdH.9gTܹs <[[[ZhA:uر#ͣB ̙3!ChիwՕ5j3uTf̘A߾}s [BL.$=zSRHKJwKHm)_3-1yФOO"Cøu}:t( r9.’hڴ)3gƲdN>{ǵkx!%Jݻ)S}ˈMߟ]vqq6mʢW^0j(tBN055eݺu޽;PKv6`am5VX<~ k̟T*ΜCJ|Mq8u;cikWY>S&RF<}! |r-ʮ]իoFݺu2d*r8q"-EǕڵkGJJ ŋgXYYlojjJ=Xb}aƍTn8cǎEsEO wD&;%&MдiSΝ;ǃ9s&*e jQ~}5k\)))L0mҽ{w`;2RӸ|؏S #5-S8:c|փ:{ޓ$287Rz95 ivwxgwM׫CN)Srٙ~_ݻs=*WLpp0;vݝ~!g ͍7 vڤbjj nݺ4mތF7f4n###4 VVVUVeȑ :FիWyrN"##s ĉT\>}DϞ=9wfbŊtڕccwo9Ç\2VmfU̎ dR&L@ ݻTX1g222;w.cǎ͍_caa9sI&tܙ>UV Ð@۹mR/?tG-\jՠ\\;'.Wm;4o/O4&Ξ=0 g7WtҜ9s 6lؐY&#G̙\FM6 qX222x!8::W_MZZ| m۶ޞ[믿DX1c7nqqql߾Z ۷oG׳o>.\VԩSt:W;4i҄GKff&={3gΐJ%hذ!$$$0n8nʄ /h"^Jtmv[Fr!f0|̢ @jb"y;JLuTo(hPjjhƁ_VlKrn`×̧~<}Gel=!rˎ{caNnَMqG*7kϙV'@:G<ڴG =%&:N$SuK155?ܜ:#ڵ+XYYѱcG7o6l гQV>@RƆ#Gp9>CHKKcʔ)XZZ2qDd׮]7|;w~7L _t)ǏoLJ/bŊʹi ‚6mгgOj5/ƆwyurUTaΜ9rYLLLX|y "{/%p!JUrOX z=iIyEDcYwyNlDwY% xX୮ڙ[gsa>^.ODg[Y^\_p8Jڨ>wFJ*sQ%T+ZmNC?}}ӗ-^<ףH߼E")WKׯ߫j*կCͥ~]wgNũK:y?gWɲ^rr+_+? yžZZPQŽZ˵c'`OsɯNeղU҅;vs~^>Xkg:"WS&*`zy|1YGNQIMH:O%=}ҫ4z;6Y6:2DD55V+*S sJWv'4@nhȐPV~6K8:p³]5`޳ȗ?~qNR8:oEn^$Y)YID ! 1g 8:~[(LOgϋpMMMjb"q\>O~ـ/`}+xԦJ}s-QFhӥۓbN(Eÿʹ.s?))>^KT!rOz%''J%IE0RN"@N>wSurpΦvTmdAycS8!{(*,gDJ/(R^$ovƻ}[t~}/_vyrYT*NFcjmSR0B q"M|1 MRSRw>W#N˖]Wm!K}66yRȵ+Qć1賳M 8m֏dz!ſp1(cbv @1h6_nG{!3+.*7,%R8&24 g/0dOqxvu;uxᨐ11A-S2]>R^蛏Dt$(.g9sk?W~ҿz=GɊnyH"B '"D\EQD$aҌN俗^+Wʇ4OSkigG˧#y ϧt(}AzJ %+*8s7oT:xA$ IDATԄD̘Kl/̜6*^ۼ!ijID䋌Tttv_̥me'1z]V:yYeUIZȐP)eJ+W+CXn]ӱfVVJG*VN'V>rMhD eD 3pW?΢lJGz)q8bDJJȑ‰QXV(Dr8:r>@(€etiK+ҳUiLLs*JEg"DRRz+WGE B(t{?L*]->V:ENDpCa\['6n᝻tdܹ)Vҙj(Hq膅 LiYܽcN\_]>O t1<_ !I '"_ؗ*)1󘫧nta"CB9y-TI,^6HSI:.LRQv-ȐPV|6?T=~Δ$ PN0.kh"wi3ǟqGJyi ;`?4d* =ϞGZRqۓB}^XetoOWm@>=iֿ1(Tz| a(2017W:00:V慪OQRl gi8))l;;wqruQ:Jqf/]t Q]:-IMLT:fV22*tSui3d1D!U1;)R甎bpR2c6L#*,\8&9W~ΔNDwmFN\Rdzzq+Ԅ{+͡ҠRY8ųM+l52VwBKa*##*ԮIhE ,t,;eAgY若->z(E7R8y*:, J*(_:$M:^srQϷ d02 OJVtS:FW6/_AW-4yO{ qeUU:V|؟t1*ZNdHSZBoC!r[JoL-,@R:FٳJEՕ+̭()R2$x kTOCc:'6naO јRґLRl*mgܼũ(Ȓ'fd(!Ϩee)#WUi϶سЗ̴4~B?//}Vm;hntmz:vNJGԾ%Cy61&U"wE Fed|$ii 3$D?ĆA7;(Gu v#DAg؟HBQQ8U6%YߚuJUcy#?0ii kγt$z"C(^-ZإsɲbTn] h{,mmi-!^* E'Sin^yb8l%t| h,8UY2|""" J)CG˖!,Q ԄL5(Ϟ'e:FQ(HDPP(RN(E[/2RSQDhbM y S:F*^,VlaANKH'2$pRzzz!Py,c7ɛ;xI2EGBD '"O$XMb 'ii32 &ffJSFj5]?Mbl,T:PX|T4iII8*Eeܽt+.UIh(W:2U+3x"Y4Ic̈́I$D?T:?'Bl(KR:PXT]48q 7O+%_dt]N.qy! tB,:ে}Oaz~(dĉuq S:xHla.TT#vIQ# P48c7ڌNJrҡ#_LǏ?(ND~$R:≌TvzuP8JcbND=3Ę",}Vȱut%=kߵ#D!&"CpruQ:))&$*C(貲h?|QؘLc(C?tO"n׃QĿLK#ec.X9vjfO*N `U>=R:5HD亨0\+(CLJ\9zÇanmt|6:/]fHMS:tsE% {A70eHxoAi)VYX&*4Lap"rUVf&q"pt?*x"B1D>LKc"_۷C8ИY4 '*. +#= (G䃈J)Cr511!,QrEtX8) 5];K ;|g#‰Uo!['tOL:f4S:bK«]c(Ɔnc?鳜߽O8"Z޹sE)'FFĽ^ΚFreRhLL R8**,3+Kle~uASr%̭ Q'EIՋIkZ t EN8"C#HtmtZ-%\"^ JH7NGGe$1)XB'p"WUi&Y਌p]P#DFT]+Eskk|zDqAZb!JGyiҶu֏ܿqS8RRl?'0bfe%8 07/-YFVFlMfRθԪtQPT(M#CgOJTI윝} % KW=,oOґ =|,fWd{ ap"D^ʢȯ:f]A!R &$rvn3+K(TBIX+tȪ8g70,ڟs/^SF\S58VT*2$[r윝S &&rr6Rei32w_By;Y'"8菥hq8j v.#F '"\;zE)ϸyyPU islFOJZbQD.xp3瑭)E c3S|-UW:J{ç[JGB)\]s"LOW:EV"=%Ç)@QU8R8I->gltuܼZtdef]c䈹{տ)PiXqć9;B"kBOǟ$3={oa0,lmS5$+3S$9]?ͽܼM85E ؅YTGz[d)0Q~P#‰z=qruQ:x We ̠b]o8j'%ʗՠ[#^CDp\b~?ONN&55333 cX?;]O;e"BaȤp"rE|T4))8UpQ:x ^ܹz]l?D 'Ѧ՚c<(RIx#B⯂FBBVb訑8:z!hڴ)ou֥Cdgg3bj֬I޽;O~?S*UD6mpqqdee,sSRxw4\"ۗJq!YT䊨pj˕U:x 賳sRnBV <>qq?nʹs簴aÆ{4eBBܜ-[bbb@bb"~~~FDиE *+s`ZUˊR/v`^NŋQn]9z(=z`߾}NɓɸJOOjzvvSwRwQQQ(DGG猉ݻh/k.233p!lݺ?#gP[]ɓ'₉ gϞԩStGGG233iРqqqܽ{֭[cnnNٲeȈ-[p BCCY~"nhu:Ցiܮ]ذa_}=z`X[[gm߸Ifiܸ1ϟganx޽3{lfΜVX[[ӢE ڴi޽{1337ߤf͚L2___z-=z: {tI^S4 "!!4.]A8}t6+f`֬Ylڴ1cưvZ:7ׯW^|;wooof~3a=rӧO wp'"W8Vư'R:AIII{L>I&MN^ϙ3gСӧOoѣG3bZj͛qww2wEVYjfԬY3ώ5?t:(^88;;pB4hЀHܹ3666hZ\\\IOOo߾+6l@Vӻwo]cNPu++5ۗu1tPsY+W>}`jjĉ8p bƍ4j###wN2er/[,o6wΎvue`!eڶm˕+Wh4Ԯ]zyx)2ep%-[Fҥ s%55[[ۜщ;wm۶{n{&MĞ={{ۗqNBL ҵsΝ;tޝ[2qD|||U ٵkӧOϙ ?5kаaC([,Çߟ5jmj;ӧLxyywBq"DUY:X.ww!::~gذa>} ~zt:ϟgժUnݚ֭[Dpp0T.SO%XM“ݻwyL>>8q3gΰvtZ-?3\pRRRXjwɓ\|???nܸ [n_~ܽ{-Z?z-+>̣G0aBReg$>ψ#bŊ|$>wwuf̘A:uSϟ'665j0vX4h@~:ueȑ 4M61dPٿ?:={2tPœz;ʊQFq%~Y&CצMԩ~~~/^ fӥY ʺPVd,쒓޽;se0r/`С 6WG ޺u={2x`z=AAAtj׎'N12烮dI o>RSSڵ+.z9`Ȉ!(GlaP+ÇX"ddd`dOrt_sA~4 ~iN}7lxoB;Mݺu|2<|bŊS# ŋœ9sܹ3w<۷~`С1~]k8)ƍd?AQZ57oΘ1cر#vY~',X@n6Ϗ;/uԉ;v_xb"""(U* SK ԙzϙ}z9XZZtRճ}b ڴiܹsi޼9FFF3&{/ݝ}?-bRlل͛7b j5fff$%%aeeErr3G ! 'q"^۽욿H[hǎ#99w})^87nn׮]9WMˑ#Gh޼9B RBL2{.eWӻ?>p|}}sÆ 㣏>b۶m4i҄K.QXF~+o+¸qU<<<ڵ+ND"33wwwf 4J޽{7nsΥA9ŭf͚GժUILL4ǏGpQ~z^lٲ=wΟ fM6g۷o(U#G$88]2gΜ;∏'66x2d666DEE1sLJ.M-5k7oBxpS:xIJbذaT*>S)Q.]z긹e6o @6mhڴ)zf͚4utt… t ڵkGvptt䣏>gggOoڎqT }Əe܀NW IDATY4c 2ѣGs /_=o?^Y痳_vvvNAoeǎ8::s3At1taǎl۶ 1c鉋 ׯϙ)(Tzm\:ǰs"l^Z#rCLL ɸp9ԩiܸ1KFRzup| ;w\r\~O>C#zɽ{(Y$cƍٻK^:QQQzj)]4*;wPjUߊ\Lf͘>}:G%** ___-[Ν;ٺu+k֬>#22(āUkrOt:ݿNaҤI,Y~={2|pzgǚ222jXYYIIIу+W`ciISKp.0ŋ3c 111aoߞL>s6l؀dffҳgOz=z޽{3ydDV#~+ 5տ~2B![#5!+b|Q]Obig+YН?2e`nn˗iԨ۷oߧM6cgg?:t[[[;… iժ'={_י9s&B ҺukMƾ}hݺ5ׯ_ʊ+V!rNk:sFj5ottZ- ݰܠ 3Vݻw)Q$%%,M||'*DEEFʕ155%55GGG222vԨQՎ222 DVSZ蠠 bcc)_S K '-p$mZF.JGo_O3U:@ӦM9w5"55???qqqᣔ(_VÇՕ bddĩSu666lJղcݝ)G/ާj\4"8Ɏy RL^qE^ҰG}rܨQ#,,,ZVrdej jw 4AypQJ{))9K RzW LGEED ;}0aѓ+jF(BI"ZA,ܼ=I~OTmgS[xīӀ !D2*!1/OПXxSRxoө}&,z,ʾzt!dĉx-YZK¿zGQU\Yl;z!PF0Iq\];Sz5Z55Q:A{j`_Qs>N0R"r236{y|K=䲋~2LɲBIFI(C&W/OB(C^gX+O﷕#l+G퇹def*GJy˃4&&sr4S&,).#Pkg'Bxx) Ԫtm;8wޝ9ҘSIq,ՕfB!^OK^MF9(ЊU^!<)!DM`ڙz]:) 9(tltB/6LFQySQ_̟˱)et'``t!D Su0'gg+Eblf{?|O\E׃v1 K4&.'=Y1'(CmF~YӰqt@-\hKBg‰"Cpvu3J?11XdG픎d|KԄD,%|tq5Ozfdi2"`*&ADlj   ýW9 ML6q%+W;2CGѶ&bԡ oOq"dT<"7l&=cP\{},jf\@th0OW\7m!6q"$I򿱰f6r^#%!8\ӹnŽٿ.ww2k'O'.*_OBWOOH[J^EWw#B%:N7}Ⅼ <꾃q)4m;9ocoа'$$rrzhۮg~$w%=%c+װϥTj܀w$qt*̬,iҧiA(DljFwqi^^PQp/.SZ8+2r̂O|3}qdLM< ;Jp?i(FrGzklR"S ETA>(F~ؔ-#&PubBðr(!w٨22ؿp %+Qk'yg S):NAUiһWM2ؖ++w$Y#I u; shJNfVD<$U7;'I>}V\#G! ocYcS s<"+=}}jwjOׯirBn  q-CsiL4skL̋EńoSt4PRJe="ߏ4K$ Dx=I"*!ɝD:::U?7wKG::Pv-_Y+d*rHGWOL']Pj"O6yLnΚI#wƱv-<$I(S(c |Dx؈HSRĈAͩN-LM; 䙞L1DZSC;c&\tAl12n4U;T) DIOIBQ$|DljZq( .UR((W&$Q>s[wI5hE?cB%%1vż !eeqe6̘-zrtܺ8ZIbE[V$ 2blZjduEFfQW"xw/^}˂xZqH(mǴIf~123cPE;Ʌh5su\hjнr(=N#B!$:NWR%wAG E7UTCUfQTbyZ-ܻ/w*6Jzu82RAG?bɄE#plJn#i[veT48 19J~Xahb,wAesqS`'kW(`&E"#'P22Ѧi-LÎ#.""ENޅM2rG8Y ]=]>eU;74DcSq޻::ABN8^)2 re!h5Cm|PfeL $3C)s]29W(/w"2Igo )[:&ϱ)]"VVgYbALtc9cw/x/ҔrG޳g#N'f þ149#A c (wC5 p;{Q!Ғ#Nr] jej*~-WӎLqR\Ir\1 I8Qef}',lmdJk$/IY#WʝD'+D<I'kW8"9v#J*ԯ'w=70h.>\ٴbIOIžO03fobiirekVG$;;8B:)wA@t`X?b UTI*6OFyas\ehI' 33+Kƭ^^ 'M,1} }sqޙ%=MxrG$I8t3fb`d~HXZԔ۠'wwM[hܫXEG#Ih [JO`TjԐ_; pŕ\rBlxE }A.AL]H;~X,XT F H ; Tmkn+H;\7m!KŰQk'iRHy]+:MA(bĉ *#lfvS!{0skkL"|l+r)-)c#t2$>!U[4; B.bĉ,>ZM<"wPi(iRm9 nr>!}f~CCV"X5~r9>z=*WFf= Źm)f%:MAHDxAY*&EİBFFry>6A)dfdC̭02;[ %51 "{b괤$/\qK8RJ|<$#-]( 8^(!ũv-R;Jܻt|+B*#S"I%%˝"}/T3{C?PXCp]JEI£Gn,:AhDxAe!h+X!w< e\!w#3P|/ [o,,tj0\7m;e84,{Itk4ܑ <ڵbN'.yݡ1bA48ryJfF8ͩnm"|HK_\Kjb"&- PBΖns1_*wz:]8)_.W,BrG)4k".2R|#\7mq99GDljvªQ-S3H;kxxq҅Gbja!w#j֯MC*ԯK8|%1ary%u̲`IQ s7 (ɾ#8kı`nmMN#F:XCBBʱ(W&]Ə; *3-?B~AO;R%eequAoI妍dx# *2αqAHtP=x) "яw2 w} qQ^7>4ߗ=>} C c 8AAގ$~7mxR|2qx3, 8bA->GV"51Q(v.C^pu!.#w A%ǗS6ce+~zvg٢Dp9cik=A'Z^ga`d$wA߹˕䎡&ee/xpQ=!_b#طp irGz鮵( h>MBWOO8s||{1LG!rGA7q"E`]8Ox'ujPcV׹ AwJQz Jqa04$˛k~ckE&&r\4c< #.2J N, oAtj~ؖ++w A˕⌁rG!2 wj/UR8 T㤰3ד:{[.r! qӹq(qrRE~Aw2a zfAxDP ֱ1-Ow5*#CKTgu(kA3(PCeUi^XȀ@r$ĐKeːJ%n_ser@GWի/яpݴƽ{`([Aw!|tC*VOJd4GN)YZ6$4#^s;;1Rp5CGWYWVx=)K\tBfȝ*e]"A8@qoP*%R [)5;7=>E_M?/b""p?wUF& ڵŶLi]|[rzՏz𐈻N xݸG?<4ĕ126¬ƴz *n!!rĀx;I!$-=ʖ*Lot߷-M厒/gW&.frGyk`WfD$BCGIOrS(/wʕ;vcbi!w*.> |7ʔ4sQ^Ys1s@R 2LH|bJhfJ%MJAZR{Y'&0މ3URҾ|\_aokG}I~CCql@( h 1R(jc'3gExnPc䛔E5h2sh5I4j㋖i숎, S+Kz̚)w=8xRO $Vh쟰pY GrRV:JVrGZ1c&J%w A8A 4$ aUh$I#&ddy?u7JhF=fb 5    q)> \"iMq"'X׈8 8I4ц ӆ)ںKQOA =q&ΊOm/hQ焏M{ z,E=-x!DIbfՇS _/h ).)X{YAAt$Ή&Q6OQ z,E=A(Ī:Hykӵ3n\$RnDd.]7m&{񖙙uR(HRii7oﶭocᮋrߺ֬ރ1=WٷO9ͻﷹh! q4zoeO} o{[ro^^4ja֯s[{ٷQUA48yBaM%$$$IdeުĤ$~??ToWևڷnnnII(nҘ5\uN}r9̥+WOyZ@zJZZK?庯RuCYn{+`8얩R:GK{^%Ideɿ9o?D=cь .]JHX(W^:D?zD\\ s.Rё& +Uc-RvZchhB捛ban 124UX/NjZ*.]&Q4ujĹReX?<}X,ŋmV+SJUj׬ĕרYg/'11v[ceiϱS'Q+Ծ=&&xrxLll,%Y|9tB\\<7~q&>&&%qeи~>Ԅpޡ~:8W @Ll,QQz,eKEf3Iw@_W_[^^{zPxqZh1?$ТiSJٓҿBa`@1K+jVCR*9HZ4mFҥOH $4[[Μ?۶ap0g/ƖZǥWT333e+ƹR%+FMhPT*^!($4W11D?Ĕ3QL7i @VVWo\Z9qccڵn\OnM6U1u0Μ?B_AfͰ!-- /Eթ\,ٰ..4kԄKcggG6msmxKJ7duj Ӷe++ѓ'գcv13{S|yBˆL,^-ZJ 116~ Ӈ}ǛbVVm }}}T*nRչ ԔvZcdd 77Us$$&ҪYslml^VщLuĤ$N&)9uPb%$I͛Q-6EGG7ojZڶlyѢԫSmah`.חkn7.V\.{zRbE룮sgCuw$221n,^-6kJHX(o]p9G=~I4i׆-;s}ҕ=ܣϠ2;f5}tà\~C!IỲÐ,֧7zz $)9cNo?Xr%F $ҕt݋F}6me(Ĥo<˗׼|{Ilޱ5jܐ}qS}ڱgK 3 غ?sBdT vz~9g5:߷?Hٴm+wݥi\v'33=>eвc{ݸ FˀĴq$w`\κ S'7ohW˯ĤDu<֬" e$e1h0dffψqcuOѠU p=wm7 I_OrرgI":WNb``ؑ#X&OАظ8fϜIՑ$m۰!z}Ig$Na&"۰p<}}}Ob9уGO?q<)ٳ%tuu2akTg8(ArJ NͲG4nۆk7nкEW `L} d5t?̠U1:m=Xv-C PoӾC Syѽ{124Ab~fPbgka$IbeINN椫+uk$?кE LD}犫+&L?՘>i%J$Es {[\۰ov}_.\D޽iҰ!vvL4s/_kPJݺg@w$ApHNҒ4˖%>!ErV/]FMʢB{z9~e+WAKdW_1k̜>+KKRRSi۷8z 1(:w.[ʖU7d^$IL=Z;U+;j:9Ibldıc)_:&Nښd5 UNܻ9WgE$GV,Ov}ܟgq v}m6𾏏z/_ƅ˗v===OL>hʣ`qtl6څWХCWd`̜>AFiV&#?u*;Ѩ~vڍY?6pq]׭K߁9q@>Fhx ;8b8:wF$4lȞlgu}IHLөW;{Pؽ? ڴlɴ(bfeXdɓ]ޡ>W.X{/~zӯWo:iqc7k6L-uNDrJ #Ϟl2tՋ;3lMˏ?#GiT+1PqjlmYt m1rx#7ݹp$k݆xu-;vŰa,-[T>OH҆:_ҳ]yR8b8vʿ۶}.:kwV\vOtAjר9s(P `mHHdddc&I]rpu_|ׯqM 1 8y~xKߞ=Ya=>"G&_{mU?WUfqd$gWNKO?Y0W~37a8VʴI_hԺY?埵-R'|c4$4DZkf6NHh(F1Ax|t@yz0{: yTuvm,׮QS7$$$v{zr']NGG'໩hҾ-))8w|O\v|wS]64 ann߆'/ZL>ٿ};EݾC9x(QQҷGu5US?F yDjjHP #cu]pT{|ux}ʔ.m*o9bccs/'W}M&߫WDFFeJ$020¼HQ(jRɝ{nv^ кEKhbd̎ س{nٳXjJys^w?s|>_OV Mw6nà b}X<ƆF *T侏6ŭQ5ww. 3#;ѴQr>wwFxd$5Ue]{m/ԭH`jlB5pE7kP o=n"5.]/0bV &ҲsG<=ppr_|:NJ 36A_RFc[DAYp֭tR4$ cuܾs RҐ$u5>q>~$':܀@)Be?oڸ1 0]y:ϗ={86,Y|ҩ3G#r>-//\ߋg@=zMν|gΞMe\cL̑cꑡ`-tx }E%%'fzݼIJJ2vˋF< O?^ZuG<>|CRL0*WȺY̜6:5k,%l`CQɉ@ 8)&X[Re  Ix߽gCsԮQ#'zztٓ1124){9Rk(U䓿E]FvN˾JѳGO䛟~d TPVsózʗ (reȕ%  @֭322I"8$11OUg\DBb6< zHiy>NZ .iokd_addHDd$ʳu,_+WSё~g1!!===zo_^W r)WEx!pÃePF:/l<4,3#L"ff I̒W3=]]tuu{Ѣ1ȞSEM|| ?-7GKNN&.>[kΜ?Osh~{y)'!PgRhݭ MoLE$,KUnjj*O>uOHLȞI]JO{$&&dso\7}=}rד11֕5˗3נrn=]JKMVwow_RV }}:i˜'T9S$L.\D'']@J^F@$Ұ^}BB0oN[yxR:s+ 47Ͼ:0EsΎ_|N6sl;J%iiu#g;={k:Q_==OH&թ矏}Z iiy6J_Ok5+ֵr}i{{*UR}iXZXLO'%%> 䃘DM2eJzR \ϟ{Tpt~:+9vԓSZI^ !qRRR\"Oc88 >1׮nmڰ~ҕHHl߳jajj=]=]Z6kƮ0#c#z̈́i_}gLW>F=f1lg-?ϛK]ޭ[y{%s~#=u(U$6@_ӧ GBↇ;QTuvVCǏ!!ȑ'hҰa}'K]4l֝;r9;w[U8hOcCb^nќ.?_Rvֹ޼;/-ZALMMr,Əe\!cFE̓*KŀQ#2~w}¼Y|u;l[Kа~}J98_OL 8$~}zcllıS's pQ$$9v6Pౣݱg9۹M7b٫DV^{,%$q=iӲ%\NR:]=]$$~Y;*Tľ,X;!!ally1wXӪys.7b``u97n(ٺ?O BRJ2^t;;[p-~;|xMHwM4TK%#!~ʕX1Y hڨ.^ ," LU&,I7, _Ȑ۴a]om[|9b=MWk>u_P(x* ~ۓrcْ|$''P"!u9kC҅LU&mZ$&6$hٴͥM׮|֫W\3Nvm)P֮Xy|k'' ?&r5ƍ~=Jp "*Mp%߫a,u.#a73Xik,cߩL@:y?N|^1GмqʗsdүWoz|џnnddfd wteN13#4<6V3cLFʒcocin.χ֖8!'TYtx:VzΌsy>\~M`c]t6\s}NC텟"1wW~_N\*ҲK'}2zpC fvH QR\^&Sv;mڌ RSq6ѽKW<.sFO$e׫`QԜtezc. GDңk7Z5kc,߰!lߵe1GI,/ZuL*mnڰ1IЪsgz})۶SX#:vCIBYh s H0ٗ[W9an.X)`1,bV,} ۶MVԭUMҨM5n̯?̾p/_ s EGsQ._Ӕ޹,JExDVVV&;p7l턅cgkk!.>RUI #2* SSSLMx$zy`RIxdv/,ѪRԏs°y1%\Zz: 'F_ORR6yZ26)9$rL26<#."N}r>3VG޹tK梣̯Cr s,CakcҺ2ES̪Xe0@Jj*?ơD9(J"(RT/&nÜ_8g+'*:J y^Qctut^S_w*Cs]uw.ihii'N0bN;BJW*)Z)bdd}YVc`hAlml_hԏ(]$:::su233)_WޜHHHxc>/9%s)"wT·8!G~Oy7DEG=)X)Ez#111unmީ#3W']:KƐ:::siKX?}䧭ܧ7?-,tplm4pFFW/6"pvFX=m͏85OM߹dbcc_8M98{t *Re0e&E]:qe"_,@K+4qLV=3欭^*EeKŲ3{qy?fKޮwu>(G8r1g8_CCڷg Ol߳;e}4Oxoݪ7Oiٵ3g#ܪTb'4i[ӫ-._&6.1c\lllr~2eɥ:t79ɂtr3g踱ܻxJcݱ_>z k+T WZUeA,U*zvȁaXoiaF3vg[OMnK?d<ڵhx~:I_ŖOlde< ԉ#GAA.^ߏ;^XM6p.ϞBrJʎaÙɧhZsާ|Ҍah4 61,<[of:'F2pM.^ʒU+?z4݋ד3|KW -G#ɑm?ʕ)'FQ>_?~ FeSϸI_V3lNN^o>gq&} mZō[7qqvfP>x>_-qtbeiEf߽juIw`?x@ʕ=t1}\ysBr|>f,!!c1¨Q*? >{Wð0:IsD9GR1zPN{+ׯS_Ȩ(cGdVU& W#N.xf-"1WoܠiKXyS p-RȨ(mقVҽ}Gzyhş;|уӉW̹<~򄦍1,u?Nrmj:-[JxD$Wĸ#)RpԮH-(8S`!Ɂ2b:={׻ϜfʹlҔI{V._Η}L^k+Z0UyOF+ ɨppgoGetx~߼8^u [wӧ NYoϡCݼȨ(ڷn00}4ocpQ1`UcmaT/j7ӾFΑ'k:BiѸ>`e.Q[hILSf/\ȥW)Sj*lݽGrA0`^TCǎ!|q.Ɔ={jYf5;GRѾUku*KݎD8pd׻~֭QQujێgÇ-r7m[ʍViNcz㺁TM\k,ZC!R;O7)[4UTEQs::Z_ O>=onx#GӼON`h$$$дs'Q U*Vq4ig'gt #>iߦ ܹq;vÇ؉e޸A{7j$GNW{֪ŧ_Ofuef̟?bٲؑ[~( я1L2eչ3 7kӹ];ng~O#tߏ3W/._F޽xI<3OUUٳXr:m̈́ᆬiFnڔW[[Sn]>ݼI4vȉгS'^X#GV /Nb_vfzyQRe~4v}zsezuL…1d7nBQ>=V_K3OOmޜ%V{0Z-pLJgd_ڷgϡC =::c\z*Z0\7myF\\m+L+VЪISjVIm}5Zaia&_F7\~ޟkС_,-ߣwڕ39{"տ6w|>jˋekVsc6rЫsW?׮5g>+K+7Nh׽;QQtߏؤK1ꣶEp-Z>]qQ;>p.śVիh׻7vڕа0<{1+3Wbbch߯qqq+]9,ٳO?aog(|[zuNBBnՍ󋍋:M~?k&{2 88z<7V%'zժSyS>xoTѝ 88ԯ[铿eKN=˲58f, ӻm5edd$ G| VcYy|5GP`AṶ۷w ..is0ѷk7zvg/~Y'~_ߡ}1W/(,_AFx ./?hG}@FTx{}֯.,wEX̲5ٽvܓaX׮|2/oo| /sqzulyݿ~S]f)+ʃ.>V-X!q< W{vϱӧT9ʱӧx(%e&l'( !\ eFAx166Q˦%&-4iА#Ǐѵ];sUvƎ{iݤ)6_ IDATy6=۷9q>>Rʭ~~9Szti-,س6㋱^5lݒ A~zӮooⰳKI0鏏Y ~cҸ}zxa}W%1_}vW4Y/]PV ^Xo?ߨ5ިYr`ԉ&|@vxwOĦ]%eڶh/[x6OMK,@L)^:7nݤV4|֯Z3޳KK ڵhZ;Z26VVj?J&@Q&k+y{٩ztww< W{r+Z }v叕ᐡ>v EIF%uiye~9ۨNZ[~)*)Q'Y'tdL7V`p>l7ݲn^俺۲YO=x1ܻ_~Fۑ0=o֪M%ذm۶ѸaC\Il7s%VxOi.7K6}\ V@mRfO.-s5><mt Bc̩{ kԨ# 9 pH91i6 O,_ .NN4uF2eu18E2tIN Oxdd45z+-inZE;[]iԿ{w9ͺ[ӵ~gx3aVåW3\w/';j*kʖVjL+Z083lsݣiF0eLujøMI޺Efьr;:y~!*_eJI;gS-߭;# ~TiX* SYzJ4iАֽz}O$UzuĠhҥolNŷ( s~eJU7 ," oIhק7 Sw?~|27kբ^TkIoѤsG""#2.,S|̐ԩQ~>$AeyIS:i֩^İ?vTn6eKhiJ%|S塟[H;9,o+s^34rMAN\:SWVy4ԭU[wyQ=fTm4ex 7p~e6Q he1bc n#y)LνATuZ4cn;jqW_acmM۲ɰI/Og3ܷ?Օj=yUK:䟁M'.?ߨ[lڄڴE. ՛;FY4hז(BͪU,Q߮ϙsg!%++,XȹK)S5dLiA^JuΒ)[%w6Sw:6I-jzvZ}~7iEݸ~}>1'޽\g[!^JmKvr42xZpaccC…  ĵpa/1(B@` ׁ&j4Qˤ p-l)S Z-Iw~4EDE@)j^pH68OpOm2bbb\|HhxNrVF`s}ueJwiB']xqju>Zr=>:9^GF-zQJov}\qOGFW58$]'twu5eB..F7Kv27{|]!{̩{E JFqbyJ7~_K0gaN3t7[+y SUF6i55kk4󝝜pvJlҥ-Eml̳՟oh-F=2O%Uovy6688''Gy]Hh1\^_bnvbpi>\̳_ގ pxnf˹-?d X *Y)YS!&'z rbҭ hf|kJ̙hNE1}ZfǺ{meź|*5_/%( NB< +-$̋)uŜҜM!!'SoOS/0=s"ɸ0BIݽ8eh^L=GSbμBs*rY1׶ !x681";EWװ"I̙ShO;bm)q* }y1c4c i eE:bmi&dĀinL=%EN33Jb-):&  T S$̋S<% $}1xAbN4̋99B璁ryQL(G~i3/Мr1ӶTR!dĈE^"(rĜy1SN)8BNN >Q%"Ỉ&snKs^7!%'zr1͎)4Ĝ1Kű)_",rB81";EH<&1g^L=bm)q* /̋7ģisSM~nBdsnKs^7!''dh^L9%ENR37М2rb^̱-O61uBdĐ͌ 1aL=%̌)uf\RGSdĀ"En9M:bm)q*BN+[g.bիv^-w_$OnEduveekKldfȭJ.B,mmPTY2"**el9~'s("Yv&D﫡h=E,VNN]46Wv1DVSAZ5{Պx"Z&"kع8v1s.bNn,Yj|,D~J'+GY]!\bpJs*["5J薿SԩT:뿓-΂R 2E'e{9%bMbXe"+M$BW?!{! Lİ)'B!^^N,SAs+$E^&)쯅BL Ȉr:>e{9Enr"#OU!{{(Ȑr|DC+$E^&) !x.B!B!D^E,>yeGxy2r{]Ms);:thqno>62$Dfv )$!;.A_z$ȯ2ysX@2CQ/\Ϡ=aߎؑĄ.&u9\uV/&u?IrN޵{!լBRw"jRes?"E I="7y H,tpДXn^J& D&dC׿~K:&/H :ʹOM';sK)eQ^=4wZ'OKr~(B]ft ܿEBSIHHԿ$:*kvp`1yK*x~6G.y+(H~1'qhq2ޫ滉ŒȔ/~a  ۽Lg4Սy?/c֏3_F!<,Gͭ}?k/%pFen\%!1Gexόޙh~W}Yo'D! $&&6?;B/NŴFҿ,7Pԭ0'LY .۽XF:f`Ѫw78e;OZUX'V-ل3l;Nٿ&#ǯ܈K7VygC)~-A_qXNSW dͲ-La,kWIIq}>%IܼОY\hFv}ז9&BSd=~~{O`͎2úѡ[R]Q]*]zKpPSf}]^Q8zhǓj4L8N=ZȞmY= 9]a~2qƉLӧ )ʉgy OBM]YF ǻgQ4Z- ԏz&oݡ9mG^0?'qD~'qOuK=x1~/ q}H3aS0$P,_0?%c 8{rE^ 1>`Duwn!uפ}Fgc@֭~E@VۊY0k*D8;B !Dvpnw_ycz)Qq-DP`0EPүu2y^ۏ-gؾ̝a= *\ w"iGޙrZnoN9KS@޸ZnSޙ=pœɥ)uHDy#iӛmGO:=r:Pĵ cQޖА\5 Y3i. XQ;>.>ռX G.m 7k;Pс)Q^!C.Ցl83q,nO~f7 .N>թR `ieA\c{~Rhں[7~ IDATQ;<²x)m;7cxXk>6.Ir[V/_>}醆+غ~/SfgXx]KA'hP՜>vVYK+K K犂+_~!ZShA fѩgkZm$DAd(Kx0/{7b@qTR3A(S$=y**( VVԉ#,Ũڧ-kWapOtH&ckkέX{Y֯BdEJﰴdkǸpu>@V?Sk<zȸVڼt^l+oGyݏvcckMe8;AXG=\s`MH;knTmfZSRi &q/99M[5@VZ8tG)^ʝ!.ZP6؟V5&Oa-C7^ҒNlL~^+Sִ$WQaF4ˤbob#", ^:0v4n67^DFMdeL3o EEz BҲ'm:4ctll{5"WW*xRlc?;P G'â(YQ\teUȲKP| IzBvTT ulY,SH!򲜎ODNɮXyħ*KB!^V8]d{9END~$)ĒB'zWraFWuʮXyħ BW!B!BU~IC#ۃ)k"/YAwĒB;j DN_ħ WuBd0[:\ɭaE~$)ĒB'2b/ODN3ND~$) !xlj aHSI| ǯUIe'҅a@SdDG"H, !x98ptOr9Qa9=H.8O5HF|MB!B!ȫ^yDɦGBBO l#\!"< `t$&&f*_~X{s}_qe""2+etܸz;y̖w˺k>cZvOٲ.c6/ߛ/ӧO2|^3Ȯ̳}z^Sǹ3WO%?O/;!˜L˔r:ry Ys-gӚݹu`|ՄQS9{r-22r.Wy!W?D!Y8i5tlI?]4e3k6FFcAMp\u*Әhta/Gwr+9}BQF޳0{N|Ϟ9~!Wbv4i /)8˚4>}ۺ}/6}:l >Aw5)Ee\XzS\L[LB߷Xy{>_z=sBk9lvt!yt.mjznyrj<]ϮU͋zmh&,k߽z*N~h57?ɲlOa< hK6ҧgVcXG5x49/+޹0s獛4O^9n]坖oɺBIz46͕7)V(=3J7KgoЪ'o]~<_뗇(;c`E^ؘ8wؾq-VFR*}#Nh~Ԫ:oyȾԪ:ը+qN9@ҧuuN]i(::v?JEVh4Zd;m;7rr]7}v>ըR<aQlp}Zf6}Q2z.C#Q {DN9Al]EҫE s-vi ;l&CٺvUkVK6{ZhټfXzJ~{mvm>LtT -%kJllX&a$Jtߖye=W*OkcX[[Nz,ec?s"n=X|;}P\qz n Oر^GVQZyZ$:[a,7SGoxg}9=d˺Z0m;7|[7@vo=Bm)T% {%J:У%~f^XX9O&m;>w84Fu™$&&ҼmCZ]r9N[tnиw$0>ЩW+rFv`XXl'%(0}p74/ ^2O?{J+nT˻C'D Wʅ[Yާ_aѺ>gcӡ=I:GɁfU5L^Ow)Dv ȳؘ8}3=`agg-5*Rv%bŲϝzϋW*E퉍g j t K VU<[Bͪ?ҫg]?p)HA^u+;qRnz=.;}>C:AܙDGSZ9:jG;{[Ǔ'O۠;xGB`}vo9݀`JvK6F}BWlj;̔{3r$8!;DǼi);66XXsE(™>y1ѱ(c\>; y$>#?-#f,(\K# dYm~<~˾<}@lLQ(,g1aT&JBBJ".u7kN+gXG ~^IHHR؁ ɋe2$4DW[J.(D2xܧ`gL]Ψ>vbwO8~,ݚap|<>2u˷Y6ϛexE&M]%&js~'GB1V֖-EZehL9I&# `!'f|;v7Jg ,mc}UM_Xʵ۲v/{ѿRuC{NRĵ;6oq W/ޢ[!\{9kq(=W"4$WnV%*Qj\>yꅛ}==ѻ Bnw1Zӭ:3ۏQĵW>yD {k|r띛m3bTlZB9~?7˾)q-'k~{+tFDX0{9ph)r玾](882kn+yΨOSM+MU?O_GC`c~fܧcЧU$7s)(DF<2'ܡ[A^͈^_һF/0u}{g'q(`GȃpzÆ]Ga\9CɲƳrT*-=`ڽ)Z{NѳՇ 9g][!6͠jDĤu!2bok3yᡑF>{ N_AQC#2a.;|@ 1ח}w,Qp(`󷿣( ^>(VѲbF4-q<}JT#%vF}BGSG8}™csJ)fԟ'OAjh4)>|< 2Bpt2}pUZm_- >1dΏ˙t{>boQU*f/seھ5N^Pag;M$eRڿ8gR\q,Wf-wz{d'}_:[w9e+ <4][pZ m?oW +Q}&goҧѥ:=~MB^Y1r-u-+vzׯyIwGѱ2@A^4Ν̦C xFjumP_~8ky[::o=~w:j0㾴炎Fiox᷋Ggqj Q^l7n&L_4eJ-6}WvTZ2 U㣯P h1q;qt1v]SB n^'.&0~^_ѷ=>gPOУ3 7v _~8KnL@;}գh~NNw5{Ս7t俓i޶{b'Нsmn\?{g=$!Rhq)RBRPC W(-EZB{p'$CdK.E̛7o޼\g)$?NaڨLlzR@>tQtӚ~`.yn[Qc73Gc>Zwn?uL_::jIVuw61}czL#bФU]H[֯'#Ӷk3Xp-gs3ϯ %;i)zśD0k(ؔb5tq:LbM[ף}{HeEkysPG*(\&>./g,gAƞӸbys /D{=vCQkȀƘ!sXTZvh5QS~ş#Gv{k\۩)L=w?zz׹Sc}a׾_O'-}*z݊xypN(1q53m6M/Ӝe߮#46Sb~ϝsmF=ZXe_j\9v:4%cU<sm >tWs([O_75핖kF[C:͘I]f4PpqwĻF`E?lJXi| @AADGӧG}4%M|rޓxRݴm׿Xi..(($$$r%DdUkTMkB#yA`E_=~[''3|j)Wʬ3#?ڱ{=F(mn%Ө :f͊߉OH|:3/]N(uoX yFc1?um~w55ݽ4nYMk@A{ۺf+z,@O3>3l^'/(6^ *VԹF)SJ;[ΟB΍3ۤ֝og? <:( RȒf1;AM3sS,,̲'[>EA6V:y 4u2/W}ٴVg[ShWoNeVK5X0kIܻ߹qGǩx @mQ}:sEG`66:XZf&=Cjj%}~Cޡ[)DGS:}B>=^Q$`l͓Նz|IO})iʂmפeWuUt7|=wq̙][EѮޟu.4#B.:N紎S.CsYBsh4:y%w6wG̱_ӿ8\]^+fX*?-MauF ͆}KquwĿ7GnmUmB"p2?dpXV$36eE4%026"$8\'ODDtrfgpr)tG(Je3O5f榄<еaxxfd4 \^H.efmӬm}8{oLÉtUt +sLLZulؙб >cssru 4}")9c@nm 4DW9O-Z |d%2j4tӖwPZun880pDOy%9Lt,*И񟥸9RO~!}m:1="2ii a!9b'>pv׎sKs}:Š34v/'J9e IDAT O]yg.@5U1Y[jjZLcF&Ftۖn_2fʔz zccִ æ|05WCQm:7`ot)@00cC~֝^\GАp=]rwnӢ]m  n\5퇴keInOcMP;!rFcP|ν a ^=QV9\gԴ v葝.RNܿBhp5n[OV#cHK `ieNίhnSi?̵|㖵w;6UӒt::X7*V `D$'뵏oRؔfjA\x mN!50py5mOI~IJfhд:kWnBLT {vz Z,7'7[g9 KJYVRSR(l?K9s2K^8s'lniC*S dql3 jz%|[.gۨ5sB#{٥k)'.֑oEnGql۰*Ց߱gs._o7ѢC# x? >.:ۘZl11}ʭzt'+w9cz㧌Z[c>9bTAf&:c|x;ֹ?w~玝-]8ssiή\mZ Iz>NJLReвCC-sgm_@)<}nZ5F*J\)[яq㡟Cahd@me?]l.~ئ!Bé3m=Oشlȭ/FƆh׀UnR(6_ߒ Aq+L׬_>vԨWGH\Iye5c!TkZݲS>OQ6-XGJ~r   m5eޤԪ_t()Ça䏱nª,3o>Y2 es.Scf-AppI,,~D&2.K&33zۅ㷵(6TJS6{%dn ׍n#EWuWUû3^br>_oϢĹ3y|ݸy $8,f=.1wnSi>lnVfWEQhS;rvaQt*!Ŵtu(N.\82?:*oS:Ћ{ѯ-}tPvy>ߛ1C>aŸz8O^-?i_o Uj{ꬍ };jɏlԬ_ m)3pv/ɻЍ7e՚9s5& 񉘙R~%v۔El^'6WMh&NIZm<_Y Y뼎Z *%m*ԉUjUӳ>=j+5{s'~?71}tj8ϧ-gԴ9cjf̅#38=[n}hי>j!~Ob|q)_S/ꠛj *BXHaQ|jpUc2ƒSG.Һ+..CȪK^KE#cCf-3XX=*9@VFƆڍI#dƿ@bbvee!atl0``˅3W GzQ:7f`8Gg;I=סLquwֵ{,9mrhI]133kNӡ`<]ZMLt:]}I[}ǾM*x&#oRTSQVYݢmצl߸7=2+Ԯ~]2sU>.Svc}~)KFyARUEA7 :{gwGjԭ)큍a\x (}!/^wcbj;!<|A嚙+߾~G)W?K@oEKܺvccW)M)oW5(;pwBR,^~8tw/g]3wRʉChX-RޮD?iTU"~f0wo=V142~><}HKS8$>Ǚ pm=]T=koc` z'Wՠ[[I׭=إY9qY;/$I ѽg*(:TE^scA8:V9G7s%m\v+$<888jfIjI9~̔hNHtT,ή%YKh:v,f4lVnݸG~ʑg Ł*albą3W3}ڴ4ֲnNjZvRS8{<7)SއXvÙX{ΐD;KEՠ[(B4 )R,HZvO/™88PAen߸NSV|nd穕9vz̑N0{[.SWԙ4'Ųab7[]38xu=ϳ,Ȍ.TS1ֵ{%Ν{L'/*jU{c*/g09IvP~z{q"/1yXV{Y_y.~^[{A._˅WY8j֯D {ko61Ȼ'v k@|tT=//j?z ~-|l x<'H{YiؼQ>z)x.RuW<ٞūSq<ٹz 88K_EGDZ}>]Xe/m}zadh󋪷PXWYSqU]A^4ƉvEDxawoCaSceeΛ:"6q" 7WNL(/ 'e } P̐BlE?:[Al/ Jfd őNBaQ` O!WAѿ    Gu](x 5aX#)\ ϋ aW!+2Bv^F?B|IAx6QAAAA\x'JZ2eM(o2¢|M|X(Έ \ σ<#GuO!_A<8ӎ d"A(,䶉2")K ³]uduiXS)A(, ć <%%ŕB_DƃPHIL7B~ ׯ"^#/7    B.m(ZB!/#B^QxRG|IAx6pDA& śB_䤐^^dyXeDSUx^oe&kHx m" aWdUrx#U ΉPUGAxvdƉEȌeDSUAxƉܱ52¢|M|X(Έ ̘AMǿ~ԕg*857tX%y'MIּdדFa .APP040"9qȋK"!Po^> 4\uk* H\B4VC"mz RUf9H5,ž-P .AxѠ?064yfVS=|'A~ѫ4E^*j55} FnsXԺBKDG1PcH2 .AxѠ?MfB&^xuSVCAxI_DYVxAh0m"7&m" PTd8ƅ~DƐPH؝a#) BnG|Z;kKƐP8 OAA SKAAA!k( Lc:2¡|M|X(Έ PTȮ:E̥ װùU"þh$'p4hRU)/"  Ed6`ǤȦ>)rGaS}}h<~/Qu =AAd7c°9=+ wEQ(hυW(aoCvPކp ~v=MZg`<~\x,,Eqo'WG>;o~GY`dlffxzc`Rfiۗ,;!?[EQCzL9_~[*r;Q>Qޡ]eJwȌAA,3Nzɷ̞&ꓘĮ-7,0--k>ʍ_0;2$''c`Mf ۨfXmbMQp(& W˦0q'=+~e1_: 2L9-kcBJԣ&o'3b= (M=q!M[7(xær&̧+033xז  vƉ"M{ptvB *04,s@{G{f~5NfOZDݸt{!ӱmIQQSk CQЈ)j!@Zoֺ1ޣoב$?Nέ`2uEn>ć xoTm]h~ղ(6:/Jw  rYuIq  >F RnE׭/,V-Ȣy+111m00_fք߮KrEArrx{p%~\+F4j^{%f'}Uh'C~  bF~kO 5a8#) BQ~D#cH(, aA( P4qV/(d1$kBqFSABfa ۧe 8^F?AA(2g CBa!k/#⟂  '2D! 'ˈ  E#(ud 8^F?AA(*d?q"W)'ˈ  EvEV"cH(,dWeDSA(\Bސ1$2DxAh0(jAAAA+((2VQ>-cH(, < o?AA(* lqؐ0/r7ǢVE(VE}H"¢(]'e5wo=%dJIN!5RRR sHyչ}Ƹz8$5\)ag2"¢ LBXhQxdq" BѠ>|[PǡNѷhy|+/r)6O{kri gҳ<~ļ_ZW_9@cFN==cB~si5oa°yOoq(Omݸ=yذz'#Ow  EE挓!NK`LC5OoW4:۫ve?Mŗbռۮ'b/W.4Fack )DF`_VZ"c044&w:φ7%9Ȉm026ylDlL%h4='55 [+exL {ݩ".6!W=KNMKMI%*2F'-;6sP1q`aiؘ8RSRu􎊈s 3e"c066"WOKa_cEGh';&`[xiDGcgoa1q|dLSSRƶu 5׳-)1q 9g:s'$$ā%>.Ągwn? cS8.Ѝ(&;5̟[Aj_5<۳]:Ƹ檟yeyʛ4FRm5l]P?+<9IVEݩ՞]9w2ƈ3x*֍nL6Z>ɩ̙jQ׿3[sϛ?U~pYvzIPë=JwU\:woOwy[x d@ThK]h07& enLZ}@oY*zKwA"e,[QLW{պ.F /m_-݅ݨښemi^-jxktEPYfȯjtW{h3oңPjuKkk2ɩj+nұ BRmQ1/<81#kkN ԑ=XI?zv {{*(Т[|h0UmM4FƯ vQשЕnmqh]Pǯ3};qp o~;7[7V+i >ߙ-u}*K۾[zp=ٽOD +spω95:2J׉Fesx)P`䀙 :VXʕlΎM{u]CMڸі-6[m?dޔQӫte :{zOUt&Ƶe ~ankr7BV|:2L4ߺ1ۺVV|;uhkRTmN}?KDuv+ӕ]9q*cۨӑUߤKkZMTD 3.b'+[7qژ>nZ>&Ae6Le6:r!ϥ&NϏyp'q 3,Tyۺy \ݚW3)7 z~]/NS?+U0y;pޠ~@W*fɼ2nOajx_'W詎zOedjoݘi]mSC#yf17MMo8HJ|×:Jtjq{AA'`5k[-lvΪ*`ڨ LrwY-`,]3;9ys'/kذw)< RRSQP83i˩[8/MtYomˇS?Wbvs%f7料>fԹUly2`xE.'6b>p$'r&؈1]C*e8uo+.#A8+Wˍ0C#Nз'1 =JnN-i\8s]~d㾥(( }{2 }~5B3|b?U"byf6?.%ܹun[ٿgqF,,c"{f`ܬw?o71zliAՇ|>[ QKC8pu='mi |}C#hдPصJyk~5㔫䏽v֩ۍk؈GSyjKA(Hkؼ~CSCC MZաaʆ2}ywؗ\M'j' t}5i5nhFKbc ~(ru126f"?kuvʘFeY:4wO' O싹 %iѶ cC]iܲ6.n]ˁ3cf7:wo9srr .{)'lKXq]<˸Yl!z6nvN١c{nVp @u[}늉%쭙ƱgTn\5=FƆxQa*Uah!ڥ]G f`ckI}ߤ7 gvTR VMdڗ6U?g|nۥ   ]l:p}2ՠχc_cCwOlߐ=c[ ssM40;v$&$1l|ll-115CWunddA`fnBFUEnzdx;6eԴA)獉+O 4L5K篱9Yo96TF5Umz۩3?7kS+@VuptmצV:h!7V˥)|4uV昛ԁh4( V'48\;t&<4KZSŧKI {k5]pe_}Hu_  g^-!>Q3 29FPzzo]9oWYxWw,iY# QXr  E2f+zRΙK J@y_^dق_xgxR}p-^ә1Ӈ(P+kK5)h46;4eHKf ǷvVXMyҁ}Pjퟞ%3 3mzڽ\\|+^6kss3,,smrǏiQ}Rc'%%Da_bu{C(][G)N93*(T(^ ӿ&z8[I( Y<уg0;_~wçtLdGuo8u[ϡsT[r+yk[+fn\tƑ;wo=Цe)~?,c '6))1pxO ؁3L>c{Ѫ+ڍrJ۔JGc:R k<[!XoRpE~j>{eSiϺdcS3Sv70g隙LWN56PvH̨QOYv9VaT[tޭ~,Ɂ'9]u>gLL(`dliwo=Cxq9|t,u׃pr)Ky'2xҺS|ڹ  xdY5H 00А4##ɓ( KS4cEi<ٰw /VӾV+SFh4s aA|>}MԥB2vql&&9dnEY<]Q"Gg{7kY#cC4 IŸa͔OϬ &?iIXYg.M'}|gh+6m/C:Sicc#RS/3޿yuQ&&9@8Ҿw/O[5th8o~IFU]r}cfiѾ!Wjǔڶ4R144Q<̘k$z*2v?U=o9u_2niq9Pq5v?~ǒyX6\iNݲjOMIIIڍRnEUn@oJ:ʠ`lbTvϑfg<;dajJ*)):>4=n7hZNaaiNي;Ҵu]~iUjȐʪu ǎ|9;_>vkgC P4ycS#\ܝ8}?8jjMz lϻ⣁ԵJ57ui3WTQܹ/< _6߀Rq5薎~6vV؈fǦtze=KfףWHuVҦLUUR.h8}좚 7u. V6ڝB \O~jhձGܫ]Ѐd~ S3:k?ۖį'յwkՏ{k:Ze;/*YECa#'1Tiu>?Ѽ]}ٓصe?u^avx׀zs'ɣ{֗  BQSGy}WwGxϟ8w2ezhvſkʣظy~#e|M ĝܸrt|ݹq.n9~eÉ "",ǂ1^nlX\Ԙr7g7k%ٴf'wn)9mMيг;b;4m]2|r䦧nPu*N 6n %$8,oR5vѷ-G/ķ'pY>.INew'=[ޅsVRj]yp!N_e8 a;)[Oz69v7BW MZ;^4#dŦzDF>f0cB% ÉWNhޮ>gO\bm9ٽk 3Xwk~|Z<\-]Ȉhtky sUu*|:j7kIv PƳ4c#}a`em3WJ5@Z+ᝑoN.φv0rʀgGw.ٷOAA(j ʠ{`emQLϗ+&êJqrQϘb2f_M +ǯOdXV;eh^[Zi&Fk\ S 5ˈzkIFLO PPhԢ}ӗclbw?5URv~%f&^;'EA!0D팛/)#sdij5>N.T[AnT熱/+ T;o3O-]Ey;N, |O,̩׸F&:ek֯ƂU;+8sllx]}쥖sXGF:i*h uX*D\B^uvkLXb++nA,Z5oUeVծOƾErJ O] `\8}E[V9\<ujP%r[&f&,"ãqp!5PܗEQ`ѪYQq5U^VuXTV.;w,{]$>]:?ѬM=555ǎ|5cQT;f$!boAlMP5ZSC)]ZZEըњU{obG#CDp I|p{s=9y3j8J: OZ>x`̏w X2 5jWb0h2Qή^7W6g̘auY{zŷ^sXsx˜lLuK&uQ1ql&Ebb"/ڎu+c_ I1n >  繗vjIt`&5h>oR[/u''{\ fu+b% 1~PT(A>8{2Hq/~\%K&!!#^`2}9#(VKC>~A/ϱԾSؘu̙zk/Ù3e9:q#IgckgK57c6r3fJ(b]Ffwʝ>} #gN'*2pȕnHWNqu}5+o YRpT`^?W cUf4Ԫ$@DDDr' ~;R̳"XOTd4{ \ +7KD"87/OV(r}}@RϷ~emk ̗8~N&quusT:G|I/{'y\X%5"""q4JΓ&:$sd<*"""b-Dr0 9JCNq)"""qwĉ!4g3wVt y=p%(l,sU%ߖ|]b% n:4 LJo} ;Ǚ!xzyp5^di83gʼn aH\ Sanyxv?{o_җ稨[2pD7ՓYf+caӪ}p3}c.GVZDaY|˃Aߗ>䣑Yz m0 rv۝-}Oۍqybb"66&ZwlƜe߱lZ P,%J穋2+L&#skuI'bDL5{{ 8@mxP;Υ+ٚRWyP6ztjxıX}t S}E2XEDr5+!8::o8w*{wFnNټOF5D{slـBERn{'ɞ(VP zdopT""""Y0RwvzFT*}t%gOR\1\\ XB術/U^8y>Uç\<'J5 w kN ïӫXkgE$KrbOFf)qu|Qb)lmm'B?ro28;;qe+Vt g.PR9va/U% [k/ۦI l=01TUw,DDD$Iץ:#YH蜑̢&"""IF諏H6]2Ꚉdi8IyH)*j%8l$#>CDDrYTDDD$;I䰺BD$Q.EuMDDDUGr,O9#EuMDDDw$Yh"6]2Ꚉd#D?KEDr"""""ɥo}AԦKfQ]#'IM ,LSj%Hvbc dUi81~Xt#?'p{w9yleRF lN#Hd_;`g]bbbӕNz 2lLmS󈌌b}35eq;{:m%"Տ=T!"""}}ĉ;do.HN|-֍G&~Ȗq="]y8w"GzYurt3 sz[&1!!}髹Ho5vsg`¿կ vwa_yMCJ.¥+lݸfOge\x jerk.̢+o*VSa56prE.OQJ"Yd=]^nM|n_`?{URBde%"!ui\Аp`k^ˇ,Reb̝'UB26ABBO>]9Sg߻سy|vŸ3v+ q}vljߘ[1:a72n./,/]cê9s>^a5?s|~&]8?c(?Y+8{*oRTg놽\ g z؍ o2IGM_S\8r \_Jl\E|cOs>htčueǓaaྣb F$^S,[8 '..[1Ep3" 0X`-ko2a yw [7dJ*{mՒ 5!{:5Wȗߝo>Ɔ50ߏC-[x=+38gz+U(?╎8 g+yy2rP0a`|>paaqqMXH8/~G2YStjڇg/a7oDЩ\a\BYd^=ٹ2I͒4\_EVjίuN?gL;?mn ˙buFbB"NNop`1V._Of=;e* ӵEz>>vPOYEώa|޷L3ENCdΖ`%|Ξ;6}:YDDDt]0Ō5W;6C,Su][>T7VWt~g'|#MoE-o'N?ǵ!?W;^}J&5y)~=Mqim1qofҼ|OѮS|~yx O/:{.YxSSN<ԤS 7 8s?Ϡ\ŒT=>[](R+)l_hٓzΖDɿ?ǑOñR}"y _v/Ц~/--_7lNBRW|?swkNT|< E&} 7s/?,s.p)2>{-ٶ;ty3szHu05o2¯d?SC}w? qsw5ˮ-kHLLYظv_hϷ~U'=L*3r^!׮ىYK1sY¯G0r ZKa+#L5j?«ujw?rOi`^S,^1Kue)!iu0.VDJ"ZޕKSrk&K~]Cll\wxP{w&.6g:7MߋCzڇnv ~ǙiюzMjZ!w{=P0 -Yea=([9Sm`cciJlQOԭ &EK"Zh34iYG':u2"3hΆFjY:Y#33߅{akkC{ڭbۭ;m/d| eR;OM} h$P ~Ӽ s4~O99\u;e)Cjb^WT!jԩ-wق_lgS~ήN4x#g,¼ܷcZqu\\sSZYv??DDDDU=.XwsόI_<<}֟'u&pvɍ}M`)WGO34<+OcM#Ң\pqߎIܹ?|b۫vx ~7HHH=kعE9&=ǜɢĻe"^;;[rz sn3L~{,y;ƴ.r.kGmKͨdӝj~[l_77k_햑n2ƾGhZy38τ}$&$r3"16w5W/pyboŚM7~~+0 BqSB`nY6&Sٔce"n$*:ۗh_7}W1L@`^|da||Pϕ;=L;2g9J5)~{߻;1޸q_ƽ3;@ͺݰ[O-h``ǕB]ƢSW|)Bh' fD^EߍdJ|SJ;@|\ly)y|w'o>dSp_u)|yn.vShT-OO*I߁/gg17/?Ixq_`{acfط--P(_ֆ۟[ry,{N!·) r~K7-'NsQzyoI!{YnQLJWlʥkI啐kJ""W:X4{1c}o^q$ۭkxSާёрVu?s97n݊e~偳 [͟=|j֩Ħ;x>0HLLym[b^8zțA̭daosV=y][R~Շè!#Nw.I#|6m>}wxkSTa>fҾϕˁS3[|n9/}y{spwvm9{`.YozyN/,ą4nQkWB. 9Spvv"EqrH^&R1sBLY`q|; ӹ'ݟe놽kU@ ;Izs{H|2uz?!Iib;}Mkxn7.g=kVms[zgۀ۲睛A>ϼ,ɕK׸§0 >6<)^wx ENТ˔XChIIiW\#^瓷QJJ+|:ԊC>T{9ϔ'9q!k|28r~9r/r9L힔(][Ѳm{_?_1'{}myJ9_նd2o/Jx \tى~58sQhbcؽ5j;W}>΅sA(Sj8z$E{77k\|wa9uZX5̉@NGي%(_E'đ(S*5sYlmm)S9̍rxW*EJILL}gq $]ri*6ߕ~7ط(7"WD+AT-ks) ͙IdD$e+`C*S܍g놽T4 ϮeӤ6g5϶}u*Q;c=HLL*-GbggGi?$?3KN=5 x.KSB mG]˄bDEFѫɸ4!8rbN-m^Vt {w!,$: D;Aw p{LU Dތ""<|QT< mm܇OղIsqr*ىooQ"ZbprG4}:~:$$$w;h388[t|<,ڻ{}'8}KRrioڏoۙpזDEFSFy cCkzd nRJs's聓IjgC ˷M#V,N]t7"9ן:[t_у{"r+;szw?;.\P& IDAT'g^J,r#U;f3<8}DDDD%]'V-͚b֩J=huEɚBѰ|Wl?sq""""B:&59q"|EDDDD#sc9YVcDŽzMjK煮HHY2.'hۀmd8;]tyܜۗI/{U%"""\:FYE"NQ.EuMDDD ]<"HS$5ԦKfQ]$w0IԦKfQ]Cꈈ<@F'P[4HM̢&"""ى.ԦKfQ]#]'#"sM̢&"""Izt,k"""ĸɪT?ERCmd&5>tWH'j+"cM̢&"""I::N 4V6O9#""""rtq""9t,k""":hV|T=ERGmd5>4Dr,O9#EuMDDDqNJ)*j%H6-C q'=4$2D:{Y;"Yɖ[7Xk"D#&lL6ΊJSIh;Mm"eViIs.0$Z;"Yog0DxkgE#\Oi8)χ|2:/!_f<-yc" fldydldI$3ZhI I,qHZވHzevω~KڈH:eC%MToDDDDDD$}t@#NХ:5""""""^`ĉ%;z>"""""""vY?|-<66*Eu xm?"oFY=YaI] -3v{a@'""""""nY~ =f0u_}q#rV ~~"!1x>t7?/y|9t"FՏ-s"""""""-:Nrr:Μ `H}ϛ\xagoGZϝFE[c|ZDxwo?䱳`D>3*c2lٰ{Ň3XEDDDDDD,qb``9sYj gA5_^2/II:ao1Q'vv/Y}3>c숩AJY/o!\<tOr-89-vrj6&r9:`kkby^F"7xV/:*8G2MIs{N\>#(VȞ10`A }o u{ѦS`cOb`-KӃ|A}? ]"""""""a2{Aü0lciR"ж+N&cɦ .lߎda7""""""^YDsUdEHFDDDDDD)[tꍈO8\"""""""b ٢D&"""""""b YĸI-I,qD?%-ToDDDDDD$}~ljꈈud@#DDDDDDDl*ˏ8100tꍈW8Ia!\BJ3 ?Y˥+'PTakg%M.L`@Kp1/kgGDDDDDD,[\cdz?ܐ +3S'өY__ϰ?æX57yiΟ J~S/3| ͓Hze'AV,xa z}$Lȼ9w.J-Fn'LJȳq'G`ogKEɝa$!!oIiӹY =^HMF+!$Wxx!ӔƕDFF_'$&pr0qqqL+ZźC-뼊g!9YJ5n݊1K:ԗ͛\bbbtsvr$냯r+: UʤNPLCDDDDDD$}E F#>6/|OBOӸRW /^ӯ0LObm#0}ט4zEo>m_kЛ/m^4ԍEZ3|oGJӸR7|i7siZI4Xj;Vnㅧ&F$e]Pֵ ϰwaʺ6!:9)cQT{wW+&|9rPpL_F9}< <{NMRtGTF­Y:-K篥FDGݲ߭E>y{,1ѱ 9E[ӼT*ВO FY&?t m*^n;qçSXWFRX|3f?t{k÷D;EZQotI?NsV2owTȾG䶈kʷ3 ~7no3&~5iFJc3o GQ| ~;ݚW@Xe&OQy=y0ǿwsK]įf1f4iQૡ|\/l?/CS%)P\t|4]/J{zi*z1|E]5|6Ep5(,e5oN51Qta~ȨT'}bU,eTbI?oƀ*5&p&@-H̯%::À/ƿ-~>w4Gi޻.E}JnlXi޿ q)SR EVlV'jxS7 ]8SmKgϒk_Yу'9w6 ll_aÑ_k^<|!M6&bcb-%<O9='J҅ٿźNWqq&,4b}47Rb2|<ݱ KD|\}Z_Χz1iTf৯2䍯 vê~+J7;YS|1w෹h͟ױjFECA/2z~7㿿Sfyw$ ^?SzW4 J[EyRT_;{qڷ~N[uӝ57sϲc8V~{{ԇ/POʔ/AX vsM0ٲm#=]'_7-SJllYF*UdJiIʃLR*{yo6}~O>>_LaDDDDDDD)we3`ckü1#9q9.oRɝ,t%*2Igc`O5zro׷~Ȍ;5G ]qB@^FJ}Y0kg,g쌏qqs~lLI#J3mH~~1m"o>7z4r\1nFD%mdpѬZwj|0{] ݕOw7ю6= Ѥe|,'?MZjU仟btyT&5qr΅A֯bQoRIw05@Ūeot|FH3e:XnRR+kgJXk뉊WqΊdSY~ @əV-mwug㬋H :Ntw̲t_9k JDe/"""""",qr5=ն>PQO\8rk'~fܹX2EN(p """"""bU٢$|kP'DJFDDDDQohѢΎƯ7@Oܹytljq|g\۷ogɒ%C…^zL&+.0aÆ qƩ֭[ 2 ի;_׏E)rYׯO.]L¦MxW2dСƚ_O#zWsNڴilۡC ?'!$$_'Ν;[… \|}eJzƍ͛)nR gϦZj<.\رc4ouA9sE9y$͚5{y7YАI͍#"""x4h} >>V^ʹix5kV fb)v5vѪU++쮆 2k֬)R$s\FXf +V̴4ػw/QQQ:u~5kвeK;F޼yi&L5k2~W^~͛ٹs#ÿ'""""""i7o^J.m~Mׯ-[d<V壵tn˛7o*-wSV.ecccQ&+W]vhтsү_GKHӸ@<3)!_|cc < C=IIɒ%Y`&#Gg޶rJ|}}9q/^[n˗yr9v튯/nJs޳XߧO^|EbbbŅeGCll,#GWWWʗ/kFHHHtV^M6m(Z(x{{Ӿ}{͛$]ԦMnիW5/L8___6lؐ,CѣGJ, Jcǎ߿?>חiӦgؼy3{B xxxPlYڴib{E=fذa-[ʕ+gC$&&ÇSbE\]]P}!,,"3gˠA86mJÆ Scƌח mllԩGؖȏ?H pwwÃz1}xsΥr8;;ӻwolBfĉu'""#FдiSmۆ...>|{իAi֬k1___sڴiM6̙3=z"i*#N 4;<@jcƍ_<@Ҽ{a :0jժELL ѣ>|d޺u={$ĉر(vI-gժU9/?uaƍL6Gyfs\ӧOW_p´nݚܹss6mڄ;/G!wbkkk1z/g_n˗ӥKbcc^:k… \ѣGk~'|Wtޝ^zwt֍HׯOV8<˖-c͚5l۶'xߟǾ}h޼9qqq\ÇD`` vϏun:L‰'Xn9hC%?{{{ʗ/ϔ)SXd ݺu؞Ȅ x"~~~i.\rXILLg/^g}ƍyWYb .GrƍǀQFpppp;w`ƍ^:AÆ )V9[oŶmhذ!5oڵ9l@@{1~sSCf'ꍈzqF'wߟ 2gΜ K"** ?+yQBf͚EB8v  22jժe?N 8::rAbbb{tWWWzCXX=zd2a5jd1b;vdƌΎ+VP\9N~7x9sXt9?RhQ?'T\s̙T_>k1efΜdЦM˗/SNejՊ%K$u6K믌?wy"cǎ1tP.\HΝ-'''謔A.]4hCβ{axzzZ?ziӦ̜9Ӣ~o=z͍2eʤyNFZcI.]Jɉ_~%'߿&?sǐ}Zvvv6_q)zDbb"׮]to…oXtσL<ӲeK~d?ɓ'2dHe„ Vl΍;b[>z-]D g4puucǎ>}G͚oc8?a@H F`^r:24Ŝ , mFtd cp\v]R 2@sP k٤M+hC)}|z~O#66rt(ݻwrJmjje˖QZZʔ)SXpe믿/l `S~~>رjNgg'6l 8{!//󎎎̜9oook#NU36ѷpB|}}~̸86sVYfKۚ5kXjqqq_ 쭪tOOO8ڵk{1L>fYiӦ3XG455`|3#222HOO^x:A'mkkYi֬Y{|yN8APPP2fLHEE/&33iooκ߃ٹs'$&&gi:^^^y@f.]Y6s[$$$ϤI7n OYn+""""Ձ8q:c2f'0|)//LJl:::nOsAmwSRRž={ $++cǎmo`߿$BCCqwwɩrz B.\C:uͩAC5j(ʕ+6Ņpߖ}]/Xx1QQQ = }O!,"""""Yj[nu()`)jk d ހٳg3g._Lcc|s9u___\]]3f |<#V%œwV#-<\K8L&ӧOǾ}qjcgȠR&--;VNN,=ji?~8K/9ԧZYDDTWW?X+++y'>u-@hh(NuZa8p> Imƶm۬͵pԺuػw/G!00`r o9>UPP@vv67˗/sU\~aBBB믿(--%!!fߞL&-ZD^^[l@O;͛d2ꫯ~!_ott4Ixx8 tuu;}!RSS,]ԡ4iŬXxFxxxc͛p٤3gBBBx"| ƍ%..pΝ;G[[6m_Ã>LfΜرcpǎצ IبgM֌7DDDD_h6bԩS1̘16TWWSTTDMM ---DFF裏2~x=zt}yۭ[d F͘1/˗_~dԩS={#GMZZ#G껲Ξ=KXX%1o<8qb}bbb)++'11`1VUU)**bmwA@@F{ڢ4>Lkk+aaa1asY جtRbbblV5kF_@FU ##cg7?Osx͢E(,,n.RSSmhnn0AO>IXX~!DDD@rr2QQQv/"gϦƌCrr2 TUUo{{=DDDGAddoS"""""""Bgg'cǎ`0P__p^d0[oM[[k׮U?@#NDDDDDDD>V^͈#٧&CЗM2?3 M#4DDDDDDDd 9}4̝;*Qܺ8CaEDDDDDDDPDDDDDDDDNDDDDDDDDPDDDDDDDDNDDDDDDDDPDDDDDDDDNDDDDDDDDPDDDDDDDDNDDDDDDDDPDDDDDDDDNDDDDDDDDT1IENDB`drumstick-2.9.0/doc/drumstick-ecosystem.qmodel0000644000175000017500000026743614541630232020543 0ustar pedropedro {47d56938-7b53-47e3-ad51-765969fce542} {68d7107b-1399-432f-9af5-edf91cde4edf} drumstick-ecosystem {b02e24be-4978-4575-9045-4252bb5b8c0c} {b02e24be-4978-4575-9045-4252bb5b8c0c} drumstick-ecosystem {e90a6748-6ab9-4521-97cd-fe505b32e046} {7726ed01-e4a7-4e0d-9322-84e778dc2c3f} library Drumstick::ALSA x:135;y:265 x:-75;y:-30;w:150;h:60 6 {69e44e37-e180-42f8-883b-0cbbd0f6bbf9} {3dad3b7a-c863-4a2c-bd91-9f6f34dd5d22} library Drumstick::File x:325;y:360 x:-70;y:-30;w:140;h:60 6 {2994929f-78c5-41d4-9e8a-91fa5efd55a1} {1d4b22db-c088-4d2e-b3de-0114fc13636e} library Drumstick::RT x:505;y:260 x:-70;y:-30;w:140;h:60 6 {dc419392-dc46-420d-8fb9-6ad679983c44} {738f1658-0bb6-497d-8345-2cd495314a08} library Drumstick::Widgets x:690;y:360 x:-85;y:-30;w:170;h:60 6 {ace9a9c0-c199-40a1-a24f-cfcb738631f7} {fcac40d5-789b-49ab-ba4a-1fb40c0a48af} application ext kmetronome x:85;y:525 x:-45;y:-25;w:90;h:50 5 false {6dcc50b0-99b0-45a5-a0b7-e42fdf2a5a13} {c295a2cc-c327-4f47-a365-3f401a859541} application ext kmidimon x:210;y:525 x:-45;y:-25;w:90;h:50 5 false {d284cca2-d46b-438f-9efb-c2b17567b7d9} {84d17b47-83b0-4d47-80a9-e78138286118} application ext dmidiplayer x:450;y:525 x:-45;y:-25;w:90;h:50 5 false {487ed391-dd29-46b0-abb7-3bf6ef825a9b} {38603669-f85f-41f0-b4bf-88bc519e5958} application ext wrk2mid x:330;y:525 x:-45;y:-25;w:90;h:50 5 false {7fa7c7ea-e29c-4660-90d8-3d427642943c} {a9dbdab5-b13d-4af0-95b2-aae83c8ec44d} application ext VMPK x:580;y:525 x:-45;y:-25;w:90;h:50 5 false {a0fef4b3-fb1b-4232-887b-b9bd549abb6b} {8e285950-f0f1-406f-bee2-8f3461aaf648} uses {ace9a9c0-c199-40a1-a24f-cfcb738631f7} {e90a6748-6ab9-4521-97cd-fe505b32e046} {660a76e4-a696-416e-8928-6057633d97d9} {d3dfa591-b798-4184-bc05-d19dbaecdf4c} uses {6dcc50b0-99b0-45a5-a0b7-e42fdf2a5a13} {e90a6748-6ab9-4521-97cd-fe505b32e046} {ce5769d2-d5c4-4b05-ad61-f40f9d31b1da} {d84e35f5-20b9-4cce-ae19-77a2591bc7a1} uses {6dcc50b0-99b0-45a5-a0b7-e42fdf2a5a13} {69e44e37-e180-42f8-883b-0cbbd0f6bbf9} {96e0bc6d-f155-4637-b43e-d0cd847fc78a} {02a572e4-e948-426c-b21a-95c6b4195540} uses {487ed391-dd29-46b0-abb7-3bf6ef825a9b} {69e44e37-e180-42f8-883b-0cbbd0f6bbf9} {c6de2eab-3279-4c72-9b09-4e8aeeb95180} {d09e7a2a-925f-4776-8975-04f8f1111ba4} uses {d284cca2-d46b-438f-9efb-c2b17567b7d9} {69e44e37-e180-42f8-883b-0cbbd0f6bbf9} {9b6aacaf-7688-4d75-8c2c-f85ae0a96593} {4f10981e-fefc-4922-af20-99c1a214b806} uses {d284cca2-d46b-438f-9efb-c2b17567b7d9} {2994929f-78c5-41d4-9e8a-91fa5efd55a1} {59b8c001-2425-4666-96f7-d38bebc76ea4} {75bf8e97-91c6-4d14-903d-89fb41659fce} uses {d284cca2-d46b-438f-9efb-c2b17567b7d9} {dc419392-dc46-420d-8fb9-6ad679983c44} {581a9749-4454-4c6e-b6ed-b71ee2d0af5d} {903bd59b-62eb-4672-96bb-ab529d7db20d} uses {2994929f-78c5-41d4-9e8a-91fa5efd55a1} {e90a6748-6ab9-4521-97cd-fe505b32e046} alsa backend {5f1fd565-98ff-4e89-9902-57c76b2aba6c} {6fdc35e3-7c68-4f2b-b67c-7b0c4bc1a483} uses {7fa7c7ea-e29c-4660-90d8-3d427642943c} {dc419392-dc46-420d-8fb9-6ad679983c44} {0e9e928c-a2b1-49c9-8f18-5ecb116c624f} {f8ba77da-cef3-49fe-a263-6878b456ba00} uses {7fa7c7ea-e29c-4660-90d8-3d427642943c} {2994929f-78c5-41d4-9e8a-91fa5efd55a1} {ab6b12b1-092f-4c69-af23-353a42267d8e} {30a7d630-9f71-493c-9420-2e6ec46e353f} library ext FluidSynth x:770;y:290 x:-60;y:-30;w:120;h:60 5 {5fd2d49a-5320-496c-845a-c2a14b9fc9e3} {129c7e9f-d97e-421f-8419-cc764c9bec15} uses {2994929f-78c5-41d4-9e8a-91fa5efd55a1} {ab6b12b1-092f-4c69-af23-353a42267d8e} fluidsynth backend {7b87f863-4bc5-46be-a706-328bf1d62d05} {a64bac38-dbb5-4177-a6c9-be471da1ca27} utils drumstick-drumgrid x:65;y:590 x:-65;y:-25;w:130;h:50 6 false {1a4d2187-6066-4a79-832c-c0df6a7c55f5} {634e32de-1aae-4e40-bbc9-ce507d6fa012} utils drumstick-dumpmid x:65;y:690 x:-65;y:-25;w:130;h:50 6 false {c842aec5-16f0-4c80-8035-e9c1e2f7cbf3} {463de80c-e142-4366-90dd-1562e2159250} utils drumstick-dumprmi x:345;y:690 x:-60;y:-25;w:120;h:50 6 false {24bee20f-b32c-4e0b-89d5-3947970433f2} {1c91a1d6-630a-46d0-8da1-e7e3fb0e0f4a} utils drumstick-dumpsmf x:350;y:590 x:-65;y:-25;w:130;h:50 6 false {f951c315-e613-41ea-9aa3-727deefef10f} {4cae26ef-3185-4127-a233-3532056ede9e} utils drumstick-dumpwrk x:350;y:640 x:-65;y:-25;w:130;h:50 6 false {7654f16f-0d5d-47ea-862b-2b4c747010db} {22e2eea2-b76b-4e8a-a958-80ea2d31ff76} utils drumstick-guiplayer x:210;y:590 x:-60;y:-25;w:120;h:50 6 false {ecf1699c-d490-453d-9492-7504d10f61d1} {cd892690-4471-4410-8013-61c5ef33fad5} utils drumstick-metronome x:60;y:640 x:-70;y:-25;w:140;h:50 6 false {5ab4394e-4271-4bcd-a15f-760a0036e768} {abaf547a-ac69-4b44-8878-e237432d2112} utils drumstick-playsmf x:210;y:640 x:-60;y:-25;w:120;h:50 6 false {920d8abf-9f68-4d8c-b54a-311e2cc382dc} {fbe33caa-d76d-43a9-b142-4d74252273a7} utils drumstick-sysinfo x:75;y:740 x:-55;y:-25;w:110;h:50 6 false {eb9f530d-3a00-43bd-a9a5-85f53073e99d} {0dc3b8ac-6209-4a0c-b263-e574b51e1f40} utils drumstick-vpiano x:580;y:590 x:-55;y:-25;w:110;h:50 6 false {65af6152-469e-4d0d-be75-28d7f00fc250} {9a757733-49f7-4e55-9bcc-c308d1c54201} uses {dc419392-dc46-420d-8fb9-6ad679983c44} {2994929f-78c5-41d4-9e8a-91fa5efd55a1} {3a67e99b-bb26-4633-8cd8-d28d1a26a1b2} {80b7aab0-c13e-42cc-bada-b832ec1f3bac} library ext Sonivox x:770;y:225 x:-55;y:-30;w:110;h:60 5 {740fe38a-bd3d-4c32-a2d1-5d46bbdbf57d} {cf94b2fb-5221-48a3-8f76-8d133d08c87e} uses {2994929f-78c5-41d4-9e8a-91fa5efd55a1} {3a67e99b-bb26-4633-8cd8-d28d1a26a1b2} sonivox backend {8fe5d783-b263-4d71-b3e4-27aa127038d3} Drumstick Family Portrait x:610;y:740 x:0;y:0;w:206.625;h:38 1 1687112362973 General {2daefbdc-4a3d-4b57-be2c-a1cc3f6f76f1} {2daefbdc-4a3d-4b57-be2c-a1cc3f6f76f1} Drumstick::ALSA {05946d7b-dfe5-4db3-a20a-4aaf4d3521b0} {05946d7b-dfe5-4db3-a20a-4aaf4d3521b0} Drumstick::File {5434f3c9-9e2e-47c2-a5ae-66114f619baf} {5434f3c9-9e2e-47c2-a5ae-66114f619baf} Drumstick::RT {b082fbdf-9a9b-494c-b06f-ac8d71611f9d} {b082fbdf-9a9b-494c-b06f-ac8d71611f9d} Drumstick::Widgets {7726ed01-e4a7-4e0d-9322-84e778dc2c3f} {7726ed01-e4a7-4e0d-9322-84e778dc2c3f} library Drumstick::ALSA {3dad3b7a-c863-4a2c-bd91-9f6f34dd5d22} {3dad3b7a-c863-4a2c-bd91-9f6f34dd5d22} library Drumstick::File {1d4b22db-c088-4d2e-b3de-0114fc13636e} {1d4b22db-c088-4d2e-b3de-0114fc13636e} library Drumstick::RT {903bd59b-62eb-4672-96bb-ab529d7db20d} {903bd59b-62eb-4672-96bb-ab529d7db20d} uses alsa backend {1d4b22db-c088-4d2e-b3de-0114fc13636e} {7726ed01-e4a7-4e0d-9322-84e778dc2c3f} {129c7e9f-d97e-421f-8419-cc764c9bec15} {129c7e9f-d97e-421f-8419-cc764c9bec15} uses fluidsynth backend {1d4b22db-c088-4d2e-b3de-0114fc13636e} {30a7d630-9f71-493c-9420-2e6ec46e353f} {cf94b2fb-5221-48a3-8f76-8d133d08c87e} {cf94b2fb-5221-48a3-8f76-8d133d08c87e} uses sonivox backend {1d4b22db-c088-4d2e-b3de-0114fc13636e} {80b7aab0-c13e-42cc-bada-b832ec1f3bac} {738f1658-0bb6-497d-8345-2cd495314a08} {738f1658-0bb6-497d-8345-2cd495314a08} library Drumstick::Widgets {9a757733-49f7-4e55-9bcc-c308d1c54201} {9a757733-49f7-4e55-9bcc-c308d1c54201} uses {738f1658-0bb6-497d-8345-2cd495314a08} {1d4b22db-c088-4d2e-b3de-0114fc13636e} {fcac40d5-789b-49ab-ba4a-1fb40c0a48af} {fcac40d5-789b-49ab-ba4a-1fb40c0a48af} application ext kmetronome {8e285950-f0f1-406f-bee2-8f3461aaf648} {8e285950-f0f1-406f-bee2-8f3461aaf648} uses {fcac40d5-789b-49ab-ba4a-1fb40c0a48af} {7726ed01-e4a7-4e0d-9322-84e778dc2c3f} {c295a2cc-c327-4f47-a365-3f401a859541} {c295a2cc-c327-4f47-a365-3f401a859541} application ext kmidimon {d3dfa591-b798-4184-bc05-d19dbaecdf4c} {d3dfa591-b798-4184-bc05-d19dbaecdf4c} uses {c295a2cc-c327-4f47-a365-3f401a859541} {7726ed01-e4a7-4e0d-9322-84e778dc2c3f} {d84e35f5-20b9-4cce-ae19-77a2591bc7a1} {d84e35f5-20b9-4cce-ae19-77a2591bc7a1} uses {c295a2cc-c327-4f47-a365-3f401a859541} {3dad3b7a-c863-4a2c-bd91-9f6f34dd5d22} {84d17b47-83b0-4d47-80a9-e78138286118} {84d17b47-83b0-4d47-80a9-e78138286118} application ext dmidiplayer {d09e7a2a-925f-4776-8975-04f8f1111ba4} {d09e7a2a-925f-4776-8975-04f8f1111ba4} uses {84d17b47-83b0-4d47-80a9-e78138286118} {3dad3b7a-c863-4a2c-bd91-9f6f34dd5d22} {4f10981e-fefc-4922-af20-99c1a214b806} {4f10981e-fefc-4922-af20-99c1a214b806} uses {84d17b47-83b0-4d47-80a9-e78138286118} {1d4b22db-c088-4d2e-b3de-0114fc13636e} {75bf8e97-91c6-4d14-903d-89fb41659fce} {75bf8e97-91c6-4d14-903d-89fb41659fce} uses {84d17b47-83b0-4d47-80a9-e78138286118} {738f1658-0bb6-497d-8345-2cd495314a08} {38603669-f85f-41f0-b4bf-88bc519e5958} {38603669-f85f-41f0-b4bf-88bc519e5958} application ext wrk2mid {02a572e4-e948-426c-b21a-95c6b4195540} {02a572e4-e948-426c-b21a-95c6b4195540} uses {38603669-f85f-41f0-b4bf-88bc519e5958} {3dad3b7a-c863-4a2c-bd91-9f6f34dd5d22} {a9dbdab5-b13d-4af0-95b2-aae83c8ec44d} {a9dbdab5-b13d-4af0-95b2-aae83c8ec44d} application ext VMPK {6fdc35e3-7c68-4f2b-b67c-7b0c4bc1a483} {6fdc35e3-7c68-4f2b-b67c-7b0c4bc1a483} uses {a9dbdab5-b13d-4af0-95b2-aae83c8ec44d} {738f1658-0bb6-497d-8345-2cd495314a08} {f8ba77da-cef3-49fe-a263-6878b456ba00} {f8ba77da-cef3-49fe-a263-6878b456ba00} uses {a9dbdab5-b13d-4af0-95b2-aae83c8ec44d} {1d4b22db-c088-4d2e-b3de-0114fc13636e} {30a7d630-9f71-493c-9420-2e6ec46e353f} {30a7d630-9f71-493c-9420-2e6ec46e353f} library ext FluidSynth {a64bac38-dbb5-4177-a6c9-be471da1ca27} {a64bac38-dbb5-4177-a6c9-be471da1ca27} utils drumstick-drumgrid {4c3a153d-4a55-4dcd-8fe1-0c6c8c370d83} {4c3a153d-4a55-4dcd-8fe1-0c6c8c370d83} {a64bac38-dbb5-4177-a6c9-be471da1ca27} {7726ed01-e4a7-4e0d-9322-84e778dc2c3f} {634e32de-1aae-4e40-bbc9-ce507d6fa012} {634e32de-1aae-4e40-bbc9-ce507d6fa012} utils drumstick-dumpmid {cd19df01-0f7e-4a6e-b721-406a9cf7a450} {cd19df01-0f7e-4a6e-b721-406a9cf7a450} {634e32de-1aae-4e40-bbc9-ce507d6fa012} {7726ed01-e4a7-4e0d-9322-84e778dc2c3f} {463de80c-e142-4366-90dd-1562e2159250} {463de80c-e142-4366-90dd-1562e2159250} utils drumstick-dumprmi {497629f1-df9f-4037-9687-c5501ec2919c} {497629f1-df9f-4037-9687-c5501ec2919c} {463de80c-e142-4366-90dd-1562e2159250} {3dad3b7a-c863-4a2c-bd91-9f6f34dd5d22} {1c91a1d6-630a-46d0-8da1-e7e3fb0e0f4a} {1c91a1d6-630a-46d0-8da1-e7e3fb0e0f4a} utils drumstick-dumpsmf {9a1cd774-8ef4-4fdc-809d-0ad4d618cfa3} {9a1cd774-8ef4-4fdc-809d-0ad4d618cfa3} {1c91a1d6-630a-46d0-8da1-e7e3fb0e0f4a} {3dad3b7a-c863-4a2c-bd91-9f6f34dd5d22} {4cae26ef-3185-4127-a233-3532056ede9e} {4cae26ef-3185-4127-a233-3532056ede9e} utils drumstick-dumpwrk {eff1644b-3cdc-4db9-93e6-bd26f9cc89a2} {eff1644b-3cdc-4db9-93e6-bd26f9cc89a2} {4cae26ef-3185-4127-a233-3532056ede9e} {3dad3b7a-c863-4a2c-bd91-9f6f34dd5d22} {22e2eea2-b76b-4e8a-a958-80ea2d31ff76} {22e2eea2-b76b-4e8a-a958-80ea2d31ff76} utils drumstick-guiplayer {6d635bf3-0198-4a3a-b764-ef506a19b033} {6d635bf3-0198-4a3a-b764-ef506a19b033} {22e2eea2-b76b-4e8a-a958-80ea2d31ff76} {7726ed01-e4a7-4e0d-9322-84e778dc2c3f} {a1399f85-d3f0-4a62-812c-90b74643c72a} {a1399f85-d3f0-4a62-812c-90b74643c72a} {22e2eea2-b76b-4e8a-a958-80ea2d31ff76} {3dad3b7a-c863-4a2c-bd91-9f6f34dd5d22} {cd892690-4471-4410-8013-61c5ef33fad5} {cd892690-4471-4410-8013-61c5ef33fad5} utils drumstick-metronome {ab26cfbd-6c82-4eb9-a61d-c50607009fb5} {ab26cfbd-6c82-4eb9-a61d-c50607009fb5} {cd892690-4471-4410-8013-61c5ef33fad5} {7726ed01-e4a7-4e0d-9322-84e778dc2c3f} {abaf547a-ac69-4b44-8878-e237432d2112} {abaf547a-ac69-4b44-8878-e237432d2112} utils drumstick-playsmf {1e01fe4b-7cfb-45f2-9b35-592f2bad5bd5} {1e01fe4b-7cfb-45f2-9b35-592f2bad5bd5} {abaf547a-ac69-4b44-8878-e237432d2112} {7726ed01-e4a7-4e0d-9322-84e778dc2c3f} {bf4fba48-b400-462a-bad0-858a4026538a} {bf4fba48-b400-462a-bad0-858a4026538a} {abaf547a-ac69-4b44-8878-e237432d2112} {3dad3b7a-c863-4a2c-bd91-9f6f34dd5d22} {fbe33caa-d76d-43a9-b142-4d74252273a7} {fbe33caa-d76d-43a9-b142-4d74252273a7} utils drumstick-sysinfo {771c007a-17ed-40e4-a961-b27a97f18edb} {771c007a-17ed-40e4-a961-b27a97f18edb} {fbe33caa-d76d-43a9-b142-4d74252273a7} {7726ed01-e4a7-4e0d-9322-84e778dc2c3f} {0dc3b8ac-6209-4a0c-b263-e574b51e1f40} {0dc3b8ac-6209-4a0c-b263-e574b51e1f40} utils drumstick-vpiano {f6379331-118a-4707-a620-d3799ac3ede5} {f6379331-118a-4707-a620-d3799ac3ede5} uses {0dc3b8ac-6209-4a0c-b263-e574b51e1f40} {1d4b22db-c088-4d2e-b3de-0114fc13636e} {489fcf99-a676-41b3-a7c5-7175d10dd637} {489fcf99-a676-41b3-a7c5-7175d10dd637} uses {0dc3b8ac-6209-4a0c-b263-e574b51e1f40} {738f1658-0bb6-497d-8345-2cd495314a08} {80b7aab0-c13e-42cc-bada-b832ec1f3bac} {80b7aab0-c13e-42cc-bada-b832ec1f3bac} library ext Sonivox drumstick-2.9.0/doc/drumstick-ecosystem.svg0000644000175000017500000075304014541630232020050 0ustar pedropedro Qt SVG Document Generated with Qt «library» Drumstick::ALSA «library» Drumstick::File «library» Drumstick::RT «library» Drumstick::Widgets «application, ext» kmetronome «application, ext» kmidimon «application, ext» dmidiplayer «application, ext» wrk2mid «application, ext» VMPK «library, ext» FluidSynth «utils» drumstick-drumgrid «utils» drumstick-dumpmid «utils» drumstick-dumprmi «utils» drumstick-dumpsmf «utils» drumstick-dumpwrk «utils» drumstick-guiplayer «utils» drumstick-metronome «utils» drumstick-playsmf «utils» drumstick-sysinfo «utils» drumstick-vpiano «library, ext» Sonivox «uses» «uses» «uses» «uses» «uses» «uses» «uses» alsa backend «uses» «uses» «uses» fluidsynth backend «uses» «uses» sonivox backend «uses» Drumstick Family Portrait Qt SVG Document drumstick-2.9.0/doc/CMakeLists.txt0000644000175000017500000000630014541630232016037 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] find_program(XSLTPROC_EXECUTABLE xsltproc) if(XSLTPROC_EXECUTABLE) message(STATUS "XSLTPROC Found: ${XSLTPROC_EXECUTABLE}") if (NOT RELEASE_DATE) execute_process ( COMMAND bash -c "LANG=en;date +'%B %d, %Y'" OUTPUT_VARIABLE RELEASE_DATE OUTPUT_STRIP_TRAILING_WHITESPACE ) endif() configure_file(drumstick-devel.doc.txt.in ${CMAKE_CURRENT_BINARY_DIR}/drumstick-devel.doc.txt IMMEDIATE @ONLY) configure_file(drumstick-drumgrid.xml.in ${CMAKE_CURRENT_BINARY_DIR}/drumstick-drumgrid.xml IMMEDIATE @ONLY) configure_file(drumstick-dumpmid.xml.in ${CMAKE_CURRENT_BINARY_DIR}/drumstick-dumpmid.xml IMMEDIATE @ONLY) configure_file(drumstick-dumpsmf.xml.in ${CMAKE_CURRENT_BINARY_DIR}/drumstick-dumpsmf.xml IMMEDIATE @ONLY) configure_file(drumstick-dumprmi.xml.in ${CMAKE_CURRENT_BINARY_DIR}/drumstick-dumprmi.xml IMMEDIATE @ONLY) configure_file(drumstick-dumpwrk.xml.in ${CMAKE_CURRENT_BINARY_DIR}/drumstick-dumpwrk.xml IMMEDIATE @ONLY) configure_file(drumstick-metronome.xml.in ${CMAKE_CURRENT_BINARY_DIR}/drumstick-metronome.xml IMMEDIATE @ONLY) configure_file(drumstick-playsmf.xml.in ${CMAKE_CURRENT_BINARY_DIR}/drumstick-playsmf.xml IMMEDIATE @ONLY) configure_file(drumstick-guiplayer.xml.in ${CMAKE_CURRENT_BINARY_DIR}/drumstick-guiplayer.xml IMMEDIATE @ONLY) configure_file(drumstick-sysinfo.xml.in ${CMAKE_CURRENT_BINARY_DIR}/drumstick-sysinfo.xml IMMEDIATE @ONLY) configure_file(drumstick-vpiano.xml.in ${CMAKE_CURRENT_BINARY_DIR}/drumstick-vpiano.xml IMMEDIATE @ONLY) include(CreateManpages) create_manpages ( ${CMAKE_CURRENT_BINARY_DIR}/drumstick-drumgrid.xml ${CMAKE_CURRENT_BINARY_DIR}/drumstick-dumpmid.xml ${CMAKE_CURRENT_BINARY_DIR}/drumstick-dumpsmf.xml ${CMAKE_CURRENT_BINARY_DIR}/drumstick-dumprmi.xml ${CMAKE_CURRENT_BINARY_DIR}/drumstick-dumpwrk.xml ${CMAKE_CURRENT_BINARY_DIR}/drumstick-metronome.xml ${CMAKE_CURRENT_BINARY_DIR}/drumstick-playsmf.xml ${CMAKE_CURRENT_BINARY_DIR}/drumstick-guiplayer.xml ${CMAKE_CURRENT_BINARY_DIR}/drumstick-sysinfo.xml ${CMAKE_CURRENT_BINARY_DIR}/drumstick-vpiano.xml ) else() message(STATUS "Warning: XSLTPROC NOT Found. Man pages won't be installed") endif() drumstick-2.9.0/doc/drumstick-devel.doc.txt.in0000644000175000017500000002761314541630232020325 0ustar pedropedro/** @mainpage drumstick Documentation @author Copyright © 2009-2023 Pedro López-Cabanillas <plcl AT users.sf.net> @date @RELEASE_DATE@ @version @PROJECT_VERSION@ This document is licensed under the Creative Commons Attribution-Share Alike 4.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/ @section Abstract This is the reference documentation for drumstick. These libraries are a set of C++ MIDI related classes, using Qt objects, idioms and style. Currently, there are four libraries designed to work together if/when needed: - \b Drumstick::ALSA is a Linux only C++/Qt wrapper around the ALSA Sequencer API. ALSA sequencer provides software support for MIDI technology on Linux. - \b Drumstick::File provides easy multiplatform file I/O for Standard MIDI Files (.mid), RIFF MIDI (.rmi) and Cakewalk (.wrk) file formats. - \b Drumstick::RT is a realtime MIDI I/O library with pluggable backends. It uses Drumstick::ALSA on Linux, and other native frameworks on macOS and Windows. - \b Drumstick::Widgets contains MIDI widgets, including a Virtual Piano used by VMPK among other programs @see https://doc.qt.io/qt-5/index.html @see https://www.alsa-project.org/alsa-doc/alsa-lib/seq.html @see https://www.ics.com/intro-design-patterns-c-qt-2nd-edition @see https://www.midi.org/articles/tutorials @section Disclaimer This document is a work in progress and it will be always in development. Please visit the drumstick web site to read the latest version. @see https://drumstick.sourceforge.io @section Introduction For an introduction to design and programming with C++ and Qt, see the book "An Introduction to Design Patterns in C++ with Qt" by by Alan Ezust and Paul Ezust. It is available published on dead trees, and also online. Drumstick::ALSA was the first library developed under the Drumstick umbrella, and is available only on Linux, because ALSA Sequencer is an exclusive Linux technology. Here is how a simple program playing notes using Drumstick::ALSA looks like: @code{.cpp} #include #include #include int main(int argc, char **argv) { QCoreApplication app(argc, argv); // create a client object on the heap drumstick::ALSA::MidiClient *client = new drumstick::ALSA::MidiClient; client->open(); client->setClientName( "MyClient" ); // create the port. The pointer is owned by the client instance drumstick::ALSA::MidiPort *port = client->createPort(); port->setPortName( "MyPort" ); port->setCapability( SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ ); port->setPortType( SND_SEQ_PORT_TYPE_MIDI_GENERIC ); // subscribe the port to some other client:port port->subscribeTo( "128:0" ); // or "name:port", like in "FluidSynth:0" QList notelist{ 60, 62, 64, 65, 67, 69, 71, 72 }; for(auto note : notelist) { // create event objects on the stack, to send note on/off messages drumstick::ALSA::NoteOnEvent ev1( 0, note, 100 ); // (channel, note number, velocity) ev1.setSource( port->getPortId() ); ev1.setSubscribers(); // deliver to all the connected ports ev1.setDirect(); // not scheduled, deliver immediately client->output( &ev1 ); // or outputDirect() if you prefer not buffered client->drainOutput(); // flush the buffer QThread::msleep(250); // wait a quarter second drumstick::ALSA::NoteOffEvent ev2( 0, note, 0 ); // (channel, note number, velocity) ev2.setSource( port->getPortId() ); ev2.setSubscribers(); // deliver to all the connected ports ev2.setDirect(); // not scheduled, deliver immediately client->output( &ev2 ); // or outputDirect() if you prefer not buffered client->drainOutput(); // flush the buffer } // close and clean client->close(); // it also deletes the port and other owned objects delete client; return 0; } @endcode MIDI is a real time protocol, so it is not a surprise that many applications using MIDI require only real time functionality. In this case, you may use the Drumstick::RT library, which is multiplatform. An example equivalent to the former one, but implemented using the Drumstick::RT library looks like this: @code{.cpp} #include #include #include #include #include int main(int argc, char **argv) { QCoreApplication app(argc, argv); drumstick::rt::BackendManager man; drumstick::rt::MIDIOutput* output = man.outputBackendByName("SonivoxEAS"); if (output != 0) { qDebug() << "testing backend: " << output->backendName(); qDebug() << "public name " << output->publicName(); auto conn = output->connections().first(); qDebug() << "port " << conn.first; output->open(conn); QList note_list{ 60, 62, 64, 65, 67, 69, 71, 72 }; for(auto midi_note : note_list) { output->sendNoteOn(0, midi_note, 100); QThread::msleep(250); // wait a quarter second output->sendNoteOff(0, midi_note, 0); } output->close(); } return 0; } @endcode A common pattern on both implementations is QThread::msleep(250) to do the rhythm. If you are targeting only Linux, you may be interested on another (better) way to do the same, using Drumstick::ALSA again, because ALSA Sequencer is capable of event scheduling (that is why it is called a Sequencer). @code{.cpp} #include #include #include #include #include int main(int argc, char **argv) { QCoreApplication app(argc, argv); drumstick::ALSA::MidiClient *client = new drumstick::ALSA::MidiClient; client->open(); client->setClientName( "MyClient" ); drumstick::ALSA::MidiPort *port = client->createPort(); port->setPortName( "MyPort" ); port->setCapability( SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ ); port->setPortType( SND_SEQ_PORT_TYPE_MIDI_GENERIC ); port->subscribeTo( "FLUID Synth (qsynth):0" ); drumstick::ALSA::MidiQueue *queue = client->createQueue( "MyQueue" ); drumstick::ALSA::QueueTempo tempo = queue->getTempo(); tempo.setPPQ( 120 ); tempo.setNominalBPM( 120 ); queue->setTempo(tempo); client->drainOutput(); queue->start(); int tick = 0; QList notelist{ 60, 62, 64, 65, 67, 69, 71, 72 }; for(auto midinote : notelist) { drumstick::ALSA::NoteOnEvent ev1( 0, midinote, 100 ); ev1.setSource( port->getPortId() ); ev1.setSubscribers(); ev1.scheduleTick(queue->getId(), tick, false); client->output( &ev1 ); tick += 60; drumstick::ALSA::NoteOffEvent ev2( 0, midinote, 0 ); ev2.setSource( port->getPortId() ); ev2.setSubscribers(); ev2.scheduleTick(queue->getId(), tick, false); client->output( &ev2 ); } client->drainOutput(); client->synchronizeOutput(); queue->stop(); // close and clean client->close(); delete client; return 0; } @endcode To build a program using Drumstick, you may use CMake or Qmake. Using CMake you need first to build or install Drumstick on your development machine, and create a project file like the following, with the name: "CMakeLists.txt" @code{.cmake} cmake_minimum_required(VERSION 3.16) project(example LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt5 COMPONENTS Core REQUIRED) find_package(Drumstick COMPONENTS ALSA REQUIRED) add_executable(example main.cpp) target_link_libraries( example Qt5::Core Drumstick::ALSA ) @endcode Assuming that you have Qt 5.12.9 installed at your $HOME/Qt directory, and Drumstick is installed at $HOME/Drumstick, then you can configure and build your project with these commands: (your current directory is your project's) @code{.sh} mkdir build cmake -S . -B build -DCMAKE_PREFIX_PATH="$HOME/Qt/5.12.9/gcc_64;$HOME/Drumstick" cmake --build build @endcode If you prefer to download the Drumstick sources and build it, you can also build your project without needing to install Drumstick. In this case: @code{.sh} mkdir build cmake -S . -B build -DCMAKE_PREFIX_PATH=$HOME/Qt/5.12.9/gcc_64 -DDrumstick_DIR=$HOME/Source/Drumstick/build cmake --build . @endcode To run your Drumstick::RT programs without installing Drumstick and your program, you may need to use an environment variable to indicate the location of the plugins, like this: @code{.sh} export DRUMSTICKRT=$HOME/Source/Drumstick/build/lib/drumstick2/ ./example @endcode There are more examples in the source tree, under the utils/ directory, and you can also see applications using this library, like kmetronome, kmidimon and VMPK. @see https://dmidiplayer.sourceforge.io @see https://kmetronome.sourceforge.io @see https://kmidimon.sourceforge.io @see https://vmpk.sourceforge.io @see https://wrk2mid.sourceforge.io @section Acknowledgments Parts of this documentation are copied from the ALSA library documentation, whose authors are:
  • Jaroslav Kysela <perex AT perex.cz>
  • Abramo Bagnara <abramo AT alsa-project.org>
  • Takashi Iwai <tiwai AT suse.de>
  • Frank van de Pol <fvdpol AT coil.demon.nl>
@defgroup ALSAGroup ALSA Sequencer Library Wrapper @brief Classes wrapping the ALSA Sequencer API @{ @defgroup ALSAClient ALSA Sequencer Clients @brief ALSA clients are any entities using ALSA sequencer services. @defgroup ALSAEvent ALSA Sequencer Events @brief MIDI Events are messages transmitted between MIDI devices or applications. @defgroup ALSAPort ALSA Sequencer Ports @brief Ports are the endpoints of the MIDI connections. @defgroup ALSAQueue ALSA Sequencer Queues @brief ALSA events are delivered to the output ports at scheduled times using queues. @defgroup ALSASubs ALSA Sequencer Subscriptions @brief Subscriptions are virtual MIDI cables between readable and writable ports. @defgroup PlayThread ALSA Sequencer Output @brief ALSA Sequencer easy playback functionality. @defgroup ALSAError ALSA Sequencer Exception @brief Exception class for ALSA Sequencer errors. @defgroup ALSATimer ALSA Timers @brief Timers provide periodic time events to applications, and also to the Sequencer. @} @defgroup File MIDI File Formats @brief Support for some MIDI File Formats @{ @defgroup SMF Standard MIDI Files Management (I/O) @brief Provides a mechanism to parse and encode Standard MIDI Files @defgroup RMID RIFF MIDI File Parser (Input) @brief Provides a mechanism to parse RIFF MIDI Files @defgroup WRK Cakewalk WRK File Parser (Input) @brief Provides a mechanism to parse Cakewalk WRK Files. @} @defgroup RT Realtime MIDI (I/O) @brief Realtime MIDI input/output multiplatform classes. @defgroup Widgets Drumstick Widgets @brief MIDI related widgets and functions. @example drumgrid.cpp Simple drum patterns @include drumgrid.h @example dumpmid.cpp Print received sequencer events @include dumpmid.h @example playsmf.cpp SMF playback, command line interface program @include playsmf.h @example guiplayer.cpp SMF playback, graphic user interface program @include guiplayer.h @example dumprmi.cpp RMI read, print and extract @include main.cpp @include dumprmi.h @example dumpsmf.cpp SMF read and print @include dumpsmf.h @example dumpwrk.cpp Cakewalk WRK file parse and print @include dumpwrk.h @example metronome.cpp Simple command line MIDI metronome @include metronome.h @example sysinfo.cpp Prints information about the ALSA sequencer subsystem @example vpiano.cpp A Virtual Piano Keyboard GUI application. See another one at http://vmpk.sourceforge.io @include vpiano.h */ drumstick-2.9.0/icons/0000755000175000017500000000000014541630232013646 5ustar pedropedrodrumstick-2.9.0/icons/drumstick.icns0000644000175000017500000006646214541630232016547 0ustar pedropedroicnsm2is325t_YhߝoHE Sqxu^GD^eJ2]htkH1>),HS_jv}]\cdSR6;DJVamt~|=Fr^*'47FKDouG$M_T?92+4IF2]cwN Wn_RuwsoWJA;&De[$(/$"-82$/8 &1oT65 !ZXG5s8mk{j ܑ;󓐐[=Lk3eE9%1il32/mR /lp X0V70Up_,GzK TԌق6Eg~nI =n>  Y^X:* CRo{x^9C_ekpv|`< $OUZ`flqw}aC7"DJPV[agmsx~y^R'AND4 '#DDEKQW]bhnty~Rr|~zcKDc90DGLRX^ciouzhxjpxovvB'bI"3DHMSY_djpjƧi.@zt]<%?DINTZ`eZ4$Q|pZ>'",3@CBGA4)!BfWMIJoq`L:,"'1981(sko}L/6.!RJC@>?BGPZ``YL8+[]Pp|E1rmhfeRXkpuxxtk[F;<9<,SlkP6Z{}~q~{sgaD 9FH7Eq}qV58  +498+ $*/5:?-  %+06;?+  !&,17<6  "'-28: #(.39IG"5 $)4_y9 8)   .S~/bB FBl8mk ;na1  Aڂ Aވt 1Ks|fG U+˒)b} ͚i!I 'm{h-w%c2it32##BeiI A; ".I {/ #w%GI'$):,, .m0-1  -v.s3D21 ]0!1$O24 4'e OkQ*NחG -ۀ#$(>{-!%C] <$<А߄~!Mf b5ƚ0 )ۚ[ Vn}Y)KؔSͭ!   h ]Ʃ[ 6ȏ CqMQی} ! 'FD8 ADE*DcsrqqC-$[xz{X 2xtoiaXND:0&BD(FGHJKMNPQSTUWXZ[]^`abdeghjklnoqrtuwxy{|& j/3Kfk_vxy-\qkd[RI>5+#AD%FGIJLMOPQSTVWYZ[]^`acdfghjkmnpqrtuwx_4S |DEGHJKMNPQRTUWXZ[]^_abdeghikLGz~}|zwsnibZRJA91*#"ADFGIJLMNPQSTVWXZ[]^`acdegh7jW~~}{yurmg`YQIA91*$ $BDEFGIJLMOPQSTVWYZ\]^`acde@ ]~}|zxtpke_XPIA:2,&  !;DEFHIKLMOPRSUVXYZ\]_`R Y‘2:- l~}|zwtoke_XQIB;4.(#  %>D EGHIKLNOQRSUVXY[N+0q T`hn]2 !Z~}{yvsoje_XRKD=71+&"  -;DEGHJKMNOQRP@$  F m0_gmsxyY&(~|{yvsoke`ZSMGA:5/*&" !*18?DCA=:6-  !  _ ^fmswz|~t " e~~}{yvsokfa\VPJD>94/+'#  %  #%'()**)('%$"   ]emrwz|~<'v~~|{yvtplhc^YSNHC>950-)&#!!$'*,.124310.,*'%# \elrvz|~X&@y~|{ywtqnjea\WRMHC?;730-*'%#" & "#&(+.1469;=>?>=<:8630-*'$ ^$\elrvz|~"%/Pl{zxurokhd_[WRMIEA>:742/-+)('&%$$#"#$%&'(*+-0358;>ADFHIGEC@=:730,($"D[dlrwz|~@ #2AHGGFECBCEFGEB?=:864310//.-,--../013468;=@CFILOQSTSRPNKHEA>950+&"O$R\dlrvz|~ /PMJHECA@><<;:9887899:;<>?ACFHKMQSVY[]^]\ZXVROLHC?94/)$A%&HS]elrvz|~Z YZWURPNLKIHGFEDC@DDEFGIKLNPSUX[]`bdefgffeca_\YVRMIC>71,&!+AKU^fmrwz|~SBC*  _zyxwvvuttssrrqpT brssttuvwxyzz{{||}||{zyxwtrokgb]XSNJGEGK  >||{{zyyxxwv uuqG>vw xxyyz{{||}}~}}|{zxvtqmie`\WTQOQT f}|{zyj5tz{{|}}~~~}|{ywurokgc`]ZYXYZ\f~}{cE ;||}~~}|{zxvspmjgecbaabd8]qux{{bB  \~e;B~~}|zyvurpnljijk@   "F`lw xgUC+  -~ ~}|{zxvtsrpopo   $|~~}|{zxwvuthy ~}||{zyyx^4 I{~}|]2  3KL;'  (+($  !.0234678:;=>?@?@: #%&')*+-./1245689:<=>@/!"$%&()*,-.01245689;<=?@;" !"$%&()+,-/01345789;<>?@0   !#$%'()+,./0234678:;<>?@)#  "#$&'(*+,./0235679:;=>?@=$ "#%&')*+-./1235679:<=>@8' !"#%&')*,-.01245689:<=?@0 )  !"$%&()*,-/01345789;<=?@?%+  !#$%'()+,-/0134678:;<>?@9 -  !#$&'(*+,./0234678:;=>?@?-  "#$&'(*+-./1235679:;=>@-- !"#%&')*+-.01245689:<=>@: -  !"$%&()*,-.01245789;<=?@?.  !"$%'()+,-/01345789;<>?@02  !#$%'()+,./0234678:;<>?@@92  "#$&'(*+,./1235679:;=>?@:1 !"#%&')*+-./1235689:<=>@:0 !"#%&()*,-.01245689:<=?;1  !"$%&()*,-/01345789;<=: 2  !#$%'()+,-/0234678:;<: c' 4  "#$&'(*+,./0234679:;L ;Q=*  "#$&')*+-./123567*3q5"( !"#%&')*+-.01245\"  %  !"$%&()*,-.01(*~Em.$  !#$%'()+,-/l_!  !#$%'(*+ 1M  "#$&'($ !"#%< !"^   8s  N   (e  =w^ SR  C  8| t8mk@    !3)! 7 q1)! H{& V҈C K̀? ?q8[  DѨwR r }s #h 5\fK0icnV Bdrumstick-2.9.0/icons/drumstick.ico0000644000175000017500000007644614541630232016370 0ustar pedropedro@@ (BF00 %nB  h hx(@ t'.(#Gq~|tI)^ I2G$379<<3$i, h~}zxusrI &`y|wX8(n *+]}~~oW{~|yuokgfh" "w|{{zyyye1hyz{{|}~~~~~~|zwrkc\WUX1 5xvusrqpoooh8+qrsuwxz{||{zwtog^SJCBF'~B ,oligeca`````abdgjmpsuuutrnibYMA7006BQ`aV5#" )^\XUROMLKKLLNORUY]bfijjhd`YQG;0&!,"2&2J\kv|}Ac5q',+('%!EHC?<976555679<?DIOTXZYWRMF>4* ,)ow>(Zju|= q4Ymxsld[RI@93.*'$#!!!!"#$'*.39?CEEB=82+$'!yL ju|O"9R~{wqi_TI>5,%    %*.00.*&"AMWEkv|p"#1~|wpg\OB6+"+9G(d7ACBA?@A=)] A*  #1.>KRoIkvtF @~|xqgZL=0$%%YADDDDDDDDEHK N QTVYV 4k&"4AEWft&B2I;+,u}zsi\L<-  21yDDDDDDDDDEGJ M PSVY\_ad-0wDDDDDDDDDDGJM PSUX[^ad!g#j#d[pALV> nGgWD2 #;DDDDDDDDDFIL O RUX[]`c f#i&l(o+r0Raw&L`n`N:$4ADDDDDDDDFIL O QTWZ]`c f"h%k(n*q-t0w0s\pRjm2UBK C'AYVv9*vk[G2 BDDDDDDDEHK N QTWZ\_be"h%k'n*q-s/v2y5|7@[g}<]Q!P, X quup)L6VorHc` ]{qm[GhWB&-kDDDDDDEHK M PSVY\_be!g$j'm)p,s/v2y4|7~:; 8U+T:Qjxy{{|{{zxt|~?. \!3>/DDDDDDGJM PSVY[^ad!g$j&m)p,r.u1x4{6~9<>9Jhuy{}}~~~~~~~~w9@ .h 4DDDDGJL O RUX[^ac f#i&l(o+r.u1x3{6}9;>==Hjn{}~~pX1)H ADDFIL O RUWZ]`c f#i%l(o+q-t0w3z5}8;>?6v==mD0DP\P?70'l 3!< CFHK N QTWZ]`be"h%k'n*q-t0w2y5|8:=??'\ G#tOw) . @K N QSVY\_be"h$k'm*p,s/v2y5|7:=??=B`J 3 > ?$U PSVY\^ad!g$j&m)p,s/v1x4{7~9??7s1Z W  &=T`c f#i%l(o+r.t0w3z6}8;>??<@w 1  5M"h%k(n*q-t0w3z5}8;=??1g6f 5 !\ $J=|)j/v2y5|7:=9z/e D&J S  "(~ ?+Q4c5e,Q: %n=  ???(0` '5HSC2m)in/+-=??<*DJH~{xuo4$2`{|fI9O /_}~~om~}ztmfd\i Xzywusra9 svxz{|}}|zwpfZPLE9 >\olheb`__\]fjnruvvtpj`RC63<JC>9* $3BS,[UPKGECDEHMSY_dggc]UI;, % ,?;$=0 ?Znz~I3%n;BGNLHB??83.+(''),06=EKOOJC;0%.$_uNmy~00v{ul_QC7,$  ")/33/*$P^j(T5my~g'h(sg|uj[I8**D$U,m;CCAAC@.e%I-  (:6JZcvN"ngD&]"<Cr}wkZF2"?4DDDDDDDHL PTX[_>&(l{yhTJD6K8)DGK OSWZ^b f#j'n+q.u2y6}9=?*dWk,Kl"y]* '+J N RVZ]ae#i&m*q.u1x5|8?9zAz 0K   6uB#h(o,r/v3z6~:>?.bAz 7b !B?BGPZ``YL8 +F[^B]XPp|EJoq`L:,"  '1981(bsBkYo}L$+Q|pZ>'"E,g3~@CBG A 4n)I !.B=Sfn~/W>MI@zt]< %T?DDDDI NTZ`e8Z)46'bI"3zDDDDH MSY_d$j)p4j_y9iK.UDc90pDDDGL RX^c#i(o.u3z9IhGx_"j,pxo5vGvB '#0DDEK QW]b"h'n-t2y8:~RUr|~zcK 1"VDJ PV[a!g&m,s1x7~<6y ^7R'KAsND|4fG $X OUZ` f%l+q0w6};?+a C07t AC_e$k*p/v5|:?-`dYk7 _h#b-?Y~i+/ٚ_Ys G@Zbg6U7iqբ[9FyvYjwMNx, xl}D 4d TDr:+Q&-6rޣ~Tw^X}jM ٻweOB D1H$_YCIOphqn3 Tnq31[UcR:zz0Rl4"Y,L?]d45/fX>d:e ( '1+E"$lnDfm)=ȋ8ϦE v " )!ѶDŽ3E2b2q*x5!hb[Tk_tƹ"XgpQ(g" i7,0@h1BTL"r H@w98*4lZf0@5D2L`FmaHoF+K\Puq*N,TK{ JwB9T]Gd hR$)DRkTOL.Bt:Iڣг" AQ//8 q I2%](GN\_[j ߥ/üh?Wi59|?w5fƏ=bfO?Z̎Cm5tFpuQ b0A z=l} W8Ϝ>2%BO׋(&,i &yd}IOvGdNrDw$IXG; [0uxeؠYPON;%A}4.+xǹDM'q`D)w 2B2#11r?an ?b0F!Zx09"b/mSwF"@*D'OH:gq|)8a|cA9h.NK}mϑrPhLW[m4l_~ǽ(aS¡8N87t_Q7ꏖtNIGK6\g羉7qĹ7R.zqnK4þ\_կ|f{I?L߿sdrumstick-2.9.0/icons/drumstick.xcf0000644000175000017500000002073014541630232016357 0ustar pedropedrogimp xcf v011@@CC gimp-commentCreated with GIMPgimp-image-grid(style solid) (fgcolor (color-rgba 0 0 0 1)) (bgcolor (color-rgba 1 1 1 1)) (xspacing 10) (yspacing 10) (spacing-unit inches) (xoffset 0) (yoffset 0) (offset-unit inches) 6 @@drumstick_64.png!? "     %$#@@@@4-*(?QceQ:%&!J|征J$5f5!=w1$Z UA`  # = 筱H /讲J k櫯U: 䬯ىQ, 6r`4쬰mB 'V# w 񮱵͏ V 2y𫮲wX  %YאָŻk"Et2+9Gd]A* .R W)! ))( ("& 2/-)&0 2 8 4-* +45,&$=jvy|zeD& $Mhknqtwz}g6!T`cfilortwz}@LX[^adgilorux{~s1$PSVY\^adgjmpsvx{~`$ @KNQSVY\_behkmpsvy|BJ3  CFHKNQTWZ]`behknqtwy|\GtO ADDFILORUWZ]`cfiloqtwz}v=mD0DP\P?70'  4DGJLORUX[^acfilorux{}=jn{}~~pX1 !3>DGJMPSVY[^adgjmprux{~9huy{}}~w9 GhWB&-DEHKMPSVY\_begjmpsvy|~8TQjxy{{|{{zxt|~?*vk[G2BDEHKNQTWZ\_behknqsvy|[Ƹ}]PXquupLoc]{q`n`N:$ADFILOQTWZ]`cfhknqtwspjUKCYv9GgWD2 ;DFILORUX[]`cfilorRw&#Aj|wm`P>-0 DGJMPSUX[^adgjdp L,u}zsi\L<- 1DEGJMPSVY\_ad5,%  %*.00.*&"MEkv|p"4Ymxsld[RI@93.*'$#!"#$'*.39?CEEB=82+$'Lju|O',+('%!EHC?<97656794* ,ĉ>Zju|= ^\XUROMLKKLLNORUY]bfijjhd`YQG;0&!,22J\kv|}A ,oligeca`abdgjmpsutrnibYMA7006BQ`aV5#5xvusrqpoh8+qrsuwxz{||{zwtog^SJCBF'"w|{{zye1hyz{{|}~|zwrkc\WUX&`y|wX8(+]}~~oW{~|yuokgfh"$379<<3$ h~}zxusrI0Gq~|tI)2'.(8 4-*  & )/257:=9/ $"%(*-0358;=??1 ! #%(+.0368;>??< !#&)+.1469<>??7 !$&),/1479<?- "$'*,/257:=??= "%'*-0258:=??'   #%(+-0358;>?6   #&(+.1369;>=  !$&),.1469<>  !$'),/247:; + "%'*-/257@go*A)y)ow ( 2/-)&0 2 8 4-  * "~n= &\S $  ΐ ! &ہ  #W  ?jW .ګ>!<w))H˺l3  .h*̍  \.@ [1.3m1n/L 0> 1p- 1Ɋ#494q3"5q*  ))c  &Ӧ" Ȭ~Bכ1 ׷n *  I2GԿi, 0^ 2t#8 00drumstick_48.png!? "     %$#0000 "Bw{\6z7=z0yd+) "/ ~D +묱iK!W㭲g;gyJ򮲷؎6ڕ  ?󫯴̂(QN *DUmeI- 6c"   j5$ ;0% "# 'G "!l{yhTJD6*+5DEILPTX\`cgkosw{~zBpy{}~~~l;!`\A DEIMQUY\`dhlptw{Kg[lyzo^r}:;saH*&DFJNRVY]aeimptx|yÒpaZglzm?0eS9!CDGKORVZ^bfimqa<*Nszp_I2?DHKOSW[_bfjm "Cr}wkZF2"4DHLPTX[_>&LpJ)&(g|uj[I8*$,;CCAAC@.%:ZNngD&0v{ul_QC7,$")/33/*$^Tmy~g'%;BGNLHB??83.+(''),06=EKOOJC;0%.uNmy~0,[UPKGECDEHMSY_dggc]UI;,%?=?Znz~I\olheb`__\]fjnruvvtpj`RC639* Xzywusra9 svxz{|}}|zwpfZPLE2`{|fI9/_}~~om~}ztmfd\-=??<*H~{xuo4#5HSC2'G " #)%" #(,/36:>?. !%),047;>?9 "&)-148;??1 #&*.158Ҫ 9ԩO i )in/+DJ$#'Ὶm' G drumstick_32.png!? "     %$#? [ s/mR/lp X0 V70Up쭴_,GzK TԌ6Eg~nI =n>  Y^X :*CRo{x^9C_ekpv|`< $OUZ`flqw}aC7 "DJPV[agmsx~y^R'AND4 '#DDEKQW]bhnty~Rr|~zcKDc90DGLRX^ciouzhxjpxovvB'bI"3DHMSY_djpjƧi.@zt]<%?DINTZ`eZ4$Q|pZ>'",3@CBGA4)!BfWMIJoq`L:,"'1981(sko}L/6.!RJC@>?BGPZ``YL8+[]Pp|E1rmhfeRXkpuxxtk[F;<9<,SlkP6Z{}~q~{sgaD 9FH7Eq}qV58  +498+$*/5:?-  %+06;?+  !&,17<6  "'-28: #(.39IG"5  $)4_y9  8)   .S~/bB FB  ;na1  Aڂ Aވ t 1 Ks|fG U+˒)b}  ͚i!I  'm{h-w%c2drumstick_16.png!? "     %$#  : R@t_YhߝoHE Sqxu^G A@D^eJ2]htkH1),HS_jv}]\cdSR6;DJVamt~|=Fr^*'47FKDouG$M_T?92+4IF2]cwNWn_RuwsoWJA;&De[$A@(/$"-82$/8&1oT65!ZX G5 A@{j ܑ;󓐐[ =L k3 eE9%1Adrumstick-2.9.0/icons/drumstick_16.png0000644000175000017500000000103614541630232016667 0ustar pedropedroPNG  IHDRasBIT|d pHYsNtEXtSoftwarewww.inkscape.org<IDAT8Kﳭ{׻" H/bD!(b)LA֭i%:B)"QT(ba(fcctק"?DU$  "rǝuΝ>bqJ|MQUD|b 4,쨅mY彅 lRgwQZެVZ ˛t9>$#@:뗸8pNPcG#Hm#esG':AY#S]+nyKEWnTӞhR7VJ d2[᚜h G3'pA\>MWȆ ;ܕ?("zIQ-֙a3\&QoASZD$D+ȑ( x+~,TRHIENDB`drumstick-2.9.0/icons/drumstick_32.png0000644000175000017500000000226214541630232016667 0ustar pedropedroPNG  IHDR szzsBIT|d pHYs77DtEXtSoftwarewww.inkscape.org</IDATX[lTEsvݶ.`[JTij"i,`4I|"!&ԄhbH0LD[Bl"`n/6>`Ce\}}sRlڜz?o CW殸і8H /Uc*O/SLbZ6.1mӒ,ۘvޜR`ʲ/w?]~^ԉ7(L$0#$HpR)5qSjUS =YUrsxFir_V B^gsbE ͪ4 8Y36Mf) CfC1 A1Rx C]čczh\E3X[tIg˶XS1m +`S)DJ$8[54װy׻_:" 7 ! ܿܢ@]佉k>!K>\K $2+^qP?PwKsնuU*j+Hm?0:e{,>m S=o?MOYH)xC^~q[\]MWU7&!1)_)%':U]K `ûhzcd]EAth]H.['/g@ QRTPfR /"dm˖ג6LuW˃Xl .ڑ4!}'hB|zҌ0iR1N}wC*h im[bֻ)%5ױl,ȃ} ɻVZA q=N(QUq cL2i^Q/!AQ*;( 55ȇNW('gK)ͻ1&#Q05zhorVK[Obωw7xz,Bf6F$Mlf Ā3?`2iE?xRtYyi+>Ç/# c!^SJY%/fVs8I2ik]6Kܕu%ʯ-u2zG7RYԄyoϪ1F B>PwHr#%H8@h>'Ckn^Nl, Z$/Q._ݷȢa`n6Mj4/nd-'4PMH@ N&* 1aQV@}yL>ӊᔢ;k*_m¾-MW٥˸u m_\)M9ڞyg M^13[X 6L`g<ǔKQتY`Z= `.K{9G xb"t\L`$OUz@v|B6&Dab"DY=uO<Y^$U(SI@- S,{~3=zz.^F21I:8N'[udN s[Fcl ғàаrsP IYCVMpgb],!3]SXd$T8YcjDg@!aYXQ%eylOR 8`b7kI˸Y?aWj* BrRr!I"E | fnjiﱀz|L418{YSl`u;2d'JNtBh+ihz#I| /Q5I^BpjRu4rZ([-yr˿O)IENDB`drumstick-2.9.0/icons/drumstick_64.png0000644000175000017500000000532214541630232016674 0ustar pedropedroPNG  IHDR@@iqsBIT|d pHYsnnУhtEXtSoftwarewww.inkscape.org< OIDATxkpUgw+٤I)i -Bb,-**2LFȀ-|((*:AGALVQZH%mڐ6iڼwͦXgݽ_lh~+[e -Vo~<[b !ld)EO>">ߏP1\ Qlc*'o 1^k:*/6hʫ"rMhr0>=Hdu %[`&. pͺ2<7\em}d͖ -1t]1t93/LnͩDY Bhg; >R~ϺX\U꺆;v h󝆦مf_| IKH; Ӱa}λyϘu+ 0l8^dϱ-G?3[Cr X)쇀2&ۯ0G]hE5G{RYOk 1ģ$h] NPy@(4YR ô0u C, Ӳ0, 0, ˲0M C,LS}l!CW^mCfOsd3l crt*1;*} mAHX- ]mcϙδ0G~QFʹ1]:V$[XA먦abDI0ccb#$eA2arR{C@vR+xּŝ#Ҧga{*J9ձ9ǁ~8QΰXJ.쿀(cU\@5qo l^t Mۚ!Au?馛;).a˨c`/|%jgyf+Y ='9$JGU@\vkvK'<'oI%w:&H`wL;ݱr$C]CCXxR$y 27VrUj9" S& $ ag3~%%V6;3I2T 052pM3e1n~3ڿ8]S}Q0h'3Ktp'«j(cfWF "`^{W@me.+FyDq7((#B=h;B5/gϐif<+0g˓INTb髇ҋr]\ds/`-FrU@9 s4|Ύ9 )ha]%|rA&w?T)S$;O$Y PYKŬinbuiX4K9r$1qq%{`bV4]*,Y6Ix`>ez (sh.E%`Z` !4&%IZ i2HԩFDŽRQ²,_. 뉄C3 zC.zGJiz z\L&'ilot B•}VlwnXPlF6 [X1fb&#; 0˶ =N3˩&BQHZѫ?2-V3L49ÀogbrhI#ig2{ tV^q묛Bp \E5PKr$ALHXa93=( ))茕qk,ɞ/}Iu&Lw,r)ĹlN\|Z~&>sU^-n@=k!3AE1=a.m/ɇZ覃M͐R:krei`,2={y{W^&.!N϶(f E7 sAKzίƜB !:A'2֍Sg(QZŽuU8E'2brqZȲHY\ :L0HVpe~ &iZH.2T],ޢB&B~;U:^'k/G!WI5WzN N/b< >6e4-pL'r"{ ڻ:ht vz4ѷP\Okɒ`dx\]d^RŝӀd$& >.4D B¢0eVa,`ζxE4[XEe40q#f^@BPǭAҌ^~ p.˥߷9Ԙ#~<[2oy~+[e -[WPWIENDB`drumstick-2.9.0/icons/CMakeLists.txt0000644000175000017500000000272114541630232016410 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] install(FILES drumstick_16.png DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/16x16/apps" RENAME drumstick.png) install(FILES drumstick_32.png DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/32x32/apps" RENAME drumstick.png) install(FILES drumstick_48.png DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48/apps" RENAME drumstick.png) install(FILES drumstick_64.png DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/64x64/apps" RENAME drumstick.png) install(FILES drumstick.svgz DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps") drumstick-2.9.0/tests/0000755000175000017500000000000014541630232013675 5ustar pedropedrodrumstick-2.9.0/tests/alsaTest1/0000755000175000017500000000000014541630232015536 5ustar pedropedrodrumstick-2.9.0/tests/alsaTest1/alsaTest1.pro0000644000175000017500000000042414541630232020121 0ustar pedropedroQT += testlib QT -= gui TARGET = alsatest1 CONFIG += c++11 cmdline TEMPLATE = app SOURCES += \ alsatest1.cpp DEFINES += SRCDIR=\\\"$$PWD/\\\" INCLUDEPATH += . ../../library/include LIBS = -L../../build/lib -ldrumstick-alsa -lasound DESTDIR = ../../build/bin drumstick-2.9.0/tests/alsaTest1/alsatest1.cpp0000644000175000017500000000575514541630232020157 0ustar pedropedro/* Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This file is part of the Drumstick project, see https://sf.net/p/drumstick This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include using namespace drumstick::ALSA; class AlsaTest1 : public QObject { Q_OBJECT public: AlsaTest1(); private Q_SLOTS: void testEvents(); }; AlsaTest1::AlsaTest1() = default; void AlsaTest1::testEvents() { NoteEvent note(0, 60, 100, 120); QCOMPARE(note.getChannel(), 0); QCOMPARE(note.getKey(), 60); QCOMPARE(note.getVelocity(), 100); QCOMPARE(note.getDuration(), 120uL); NoteOnEvent noteOn(1, 60, 100); QCOMPARE(noteOn.getChannel(), 1); QCOMPARE(noteOn.getKey(), 60); QCOMPARE(noteOn.getVelocity(), 100); NoteOffEvent noteOff(2, 60, 0); QCOMPARE(noteOff.getChannel(), 2); QCOMPARE(noteOff.getKey(), 60); QCOMPARE(noteOff.getVelocity(), 0); ControllerEvent ctl(3, 33, 66); QCOMPARE(ctl.getChannel(), 3); QCOMPARE(ctl.getParam(), 33u); QCOMPARE(ctl.getValue(), 66); ProgramChangeEvent pgm(4, 123); QCOMPARE(pgm.getChannel(), 4); QCOMPARE(pgm.getValue(), 123); KeyPressEvent keyPress(5, 60, 124); QCOMPARE(keyPress.getChannel(), 5); QCOMPARE(keyPress.getKey(), 60); QCOMPARE(keyPress.getVelocity(), 124); ChanPressEvent chanPress(6, 111); QCOMPARE(chanPress.getChannel(), 6); QCOMPARE(chanPress.getValue(), 111); PitchBendEvent bender(7, 1234); QCOMPARE(bender.getChannel(), 7); QCOMPARE(bender.getValue(), 1234); QByteArray sysexData = QByteArray::fromHex("f04110421240007f0041f7"); SysExEvent sysexEvent(sysexData); QCOMPARE(sysexEvent.getData(), sysexData.data()); QCOMPARE(sysexEvent.getLength(), (unsigned) sysexData.length()); SysExEvent otherEvent = sysexEvent; QCOMPARE(otherEvent.getData(), sysexData.data()); QCOMPARE(otherEvent.getLength(), (unsigned) sysexData.length()); QString text = "This can be a copyright, song name, instrument, lyric..."; TextEvent textEvent(text, 3); QCOMPARE(textEvent.getText(), text); QCOMPARE(textEvent.getLength(), (unsigned) text.length()); TextEvent otherText = textEvent; QCOMPARE(otherText.getText(), text); QCOMPARE(otherText.getLength(), (unsigned) text.length()); } QTEST_APPLESS_MAIN(AlsaTest1) #include "alsatest1.moc" drumstick-2.9.0/tests/alsaTest1/CMakeLists.txt0000644000175000017500000000210314541630232020272 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] add_executable (alsaTest1 alsatest1.cpp) target_link_libraries (alsaTest1 PRIVATE Drumstick::ALSA Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Test ) add_test (alsaTest1 ${PROJECT_BINARY_DIR}/bin/alsaTest1) drumstick-2.9.0/tests/alsaTest2/0000755000175000017500000000000014541630232015537 5ustar pedropedrodrumstick-2.9.0/tests/alsaTest2/alsaTest2.pro0000644000175000017500000000042414541630232020123 0ustar pedropedroQT += testlib QT -= gui TARGET = alsaTest2 CONFIG += c++11 cmdline TEMPLATE = app SOURCES += \ alsatest2.cpp DEFINES += SRCDIR=\\\"$$PWD/\\\" INCLUDEPATH += . ../../library/include LIBS = -L../../build/lib -ldrumstick-alsa -lasound DESTDIR = ../../build/bin drumstick-2.9.0/tests/alsaTest2/alsatest2.cpp0000644000175000017500000000662214541630232020153 0ustar pedropedro/* Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This file is part of the Drumstick project, see https://sf.net/p/drumstick This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include using namespace drumstick::ALSA; class AlsaTest2 : public QObject, public TimerEventHandler { Q_OBJECT public: AlsaTest2(); // TimerEventHandler implementation void handleTimerEvent(int ticks, int msecs) override; private Q_SLOTS: void testTimer(); void initTestCase(); void cleanupTestCase(); private: QPointer m_test_timer; int m_count; }; AlsaTest2::AlsaTest2(): m_test_timer(nullptr), m_count(0) { } void AlsaTest2::handleTimerEvent(int , int ) { m_count++; } void AlsaTest2::initTestCase() { try { QFileInfo check_devsnd("/dev/snd/"); QVERIFY(check_devsnd.exists() && check_devsnd.isDir()); QFileInfo check_devsndseq("/dev/snd/seq"); QVERIFY(check_devsndseq.exists() && !check_devsndseq.isFile() && !check_devsndseq.isDir()); QFileInfo check_devsndtimer("/dev/snd/timer"); QVERIFY(check_devsndtimer.exists() && !check_devsndseq.isFile() && !check_devsndseq.isDir()); m_test_timer = Timer::bestGlobalTimer( SND_TIMER_OPEN_NONBLOCK | SND_TIMER_OPEN_TREAD ); } catch (...) { qWarning("Timer test initialization failed"); } } void AlsaTest2::cleanupTestCase() { delete m_test_timer; } void AlsaTest2::testTimer() { if (m_test_timer != nullptr) { m_count = 0; try { TimerParams tparams; TimerInfo tinfo = m_test_timer->getTimerInfo(); tparams.setAutoStart(true); if (!tinfo.isSlave()) { /* 50 Hz */ tparams.setTicks( 1000000000L / tinfo.getResolution() / 50); if (tparams.getTicks() < 1) { tparams.setTicks(1); } } else { tparams.setTicks(1); } tparams.setFilter(1 << SND_TIMER_EVENT_TICK); m_test_timer->setTimerParams(tparams); m_test_timer->setHandler(this); // Testing timer callback method m_test_timer->start(); m_test_timer->startEvents(); QTest::qWait(1000); m_test_timer->stopEvents(); m_test_timer->stop(); QVERIFY2(qAbs(50 - m_count) <= 1, "Timer results are wrong"); TimerStatus tstatus = m_test_timer->getTimerStatus(); QCOMPARE(tstatus.getLost(), 0L); QCOMPARE(tstatus.getOverrun(), 0L); } catch (...) { QFAIL("Timer test failed"); } } } QTEST_GUILESS_MAIN(AlsaTest2) #include "alsatest2.moc" drumstick-2.9.0/tests/alsaTest2/CMakeLists.txt0000644000175000017500000000225514541630232020303 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] add_executable (alsaTest2 alsatest2.cpp) target_link_libraries (alsaTest2 PRIVATE Drumstick::ALSA Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Test ) if ((EXISTS "/dev/snd/") AND (EXISTS "/dev/snd/seq") AND (EXISTS "/dev/snd/timer")) add_test (alsaTest2 ${PROJECT_BINARY_DIR}/bin/alsaTest2) endif() drumstick-2.9.0/tests/fileTest1/0000755000175000017500000000000014541630232015535 5ustar pedropedrodrumstick-2.9.0/tests/fileTest1/filetest1.cpp0000644000175000017500000002403114541630232020141 0ustar pedropedro/* Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This file is part of the Drumstick project, see https://sf.net/p/drumstick This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include #include #include DISABLE_WARNING_PUSH DISABLE_WARNING_DEPRECATED_DECLARATIONS using namespace drumstick::File; class FileTest1 : public QObject { Q_OBJECT public: explicit FileTest1(QObject* parent = nullptr); static const char test_mid[]; static const int test_mid_len; static const int FORMAT; static const int TRACKS; static const int DIVISION; static const int TEMPO; static const QString COPYRIGHT; static const QByteArray GSRESET; static const QList NOTES; public Q_SLOTS: void errorHandler(const QString& errorStr); void trackHandler(int track); void headerEvent(int format, int ntrks, int division); void trackStartEvent(); void trackEndEvent(); void endOfTrackEvent(); void noteOnEvent(int chan, int pitch, int vol); void noteOffEvent(int chan, int pitch, int vol); void keyPressEvent(int chan, int pitch, int press); void ctlChangeEvent(int chan, int ctl, int value); void pitchBendEvent(int chan, int value); void programEvent(int chan, int patch); void chanPressEvent(int chan, int press); void sysexEvent(const QByteArray& data); void textEvent(int typ, const QString& data); void timeSigEvent(int b0, int b1, int b2, int b3); void keySigEvent(int b0, int b1); void tempoEvent(int tempo); private Q_SLOTS: void testCaseWriteSmf(); void testCaseReadSmf(); void initTestCase(); void cleanupTestCase(); private: QSmf *m_engine; int m_numNoteOn; int m_lastNoteOn; int m_numNoteOff; int m_lastNoteOff; int m_lastKeyPress; int m_currentTrack; int m_endOfTrack; int m_lastCtl; int m_lastProgram; int m_lastChanPress; int m_lastPitchBend; int m_lastTempo; QByteArray m_testData; QByteArray m_lastSysex; QString m_lastError; QString m_header; QString m_trackEnd; QString m_lastTextEvent; QString m_lastTimeSig; QString m_lastKeySig; }; FileTest1::FileTest1(QObject* parent): QObject(parent), m_engine(nullptr), m_numNoteOn(0), m_lastNoteOn(0), m_numNoteOff(0), m_lastNoteOff(0), m_lastKeyPress(0), m_currentTrack(0), m_endOfTrack(0), m_lastCtl(0), m_lastProgram(0), m_lastChanPress(0), m_lastPitchBend(0), m_lastTempo(0) { m_engine = new QSmf(this); m_engine->setTextCodec(QTextCodec::codecForName("UTF-8")); connect(m_engine, &QSmf::signalSMFError, this, &FileTest1::errorHandler); connect(m_engine, &QSmf::signalSMFWriteTrack, this, &FileTest1::trackHandler); connect(m_engine, &QSmf::signalSMFHeader, this, &FileTest1::headerEvent); connect(m_engine, &QSmf::signalSMFTrackStart, this, &FileTest1::trackStartEvent); connect(m_engine, &QSmf::signalSMFTrackEnd, this, &FileTest1::trackEndEvent); connect(m_engine, &QSmf::signalSMFNoteOn, this, &FileTest1::noteOnEvent); connect(m_engine, &QSmf::signalSMFNoteOff, this, &FileTest1::noteOffEvent); connect(m_engine, &QSmf::signalSMFKeyPress, this, &FileTest1::keyPressEvent); connect(m_engine, &QSmf::signalSMFCtlChange, this, &FileTest1::ctlChangeEvent); connect(m_engine, &QSmf::signalSMFPitchBend, this, &FileTest1::pitchBendEvent); connect(m_engine, &QSmf::signalSMFProgram, this, &FileTest1::programEvent); connect(m_engine, &QSmf::signalSMFChanPress, this, &FileTest1::chanPressEvent); connect(m_engine, &QSmf::signalSMFSysex, this, &FileTest1::sysexEvent); connect(m_engine, &QSmf::signalSMFText, this, &FileTest1::textEvent); connect(m_engine, &QSmf::signalSMFendOfTrack, this, &FileTest1::endOfTrackEvent); connect(m_engine, &QSmf::signalSMFTimeSig, this, &FileTest1::timeSigEvent); connect(m_engine, &QSmf::signalSMFKeySig, this, &FileTest1::keySigEvent); connect(m_engine, &QSmf::signalSMFTempo, this, &FileTest1::tempoEvent); } const char FileTest1::test_mid[] = { '\x4d','\x54','\x68','\x64','\x00','\x00','\x00','\x06','\x00','\x00','\x00','\x01', '\x00','\x78','\x4d','\x54','\x72','\x6b','\x00','\x00','\x00','\x99','\x00','\xff', '\x02','\x2f','\x43','\x6f','\x70','\x79','\x72','\x69','\x67','\x68','\x74','\x20', '\x28','\x43','\x29','\x20','\x32','\x30','\x30','\x36','\x2d','\x32','\x30','\x32', '\x33','\x20','\x50','\x65','\x64','\x72','\x6f','\x20','\x4c','\xc3','\xb3','\x70', '\x65','\x7a','\x2d','\x43','\x61','\x62','\x61','\x6e','\x69','\x6c','\x6c','\x61', '\x73','\x00','\xff','\x51','\x03','\x09','\x27','\xc0','\x00','\xff','\x58','\x04', '\x03','\x02','\x24','\x08','\x00','\xff','\x59','\x02','\x02','\x00','\x00','\xf0', '\x0a','\x41','\x10','\x42','\x12','\x40','\x00','\x7f','\x00','\x41','\xf7','\x00', '\x90','\x3c','\x78','\x3c','\x80','\x3c','\x00','\x00','\x90','\x3e','\x78','\x3c', '\x80','\x3e','\x00','\x00','\x90','\x40','\x78','\x3c','\x80','\x40','\x00','\x00', '\x90','\x41','\x78','\x3c','\x80','\x41','\x00','\x00','\x90','\x43','\x78','\x3c', '\x80','\x43','\x00','\x00','\x90','\x45','\x78','\x3c','\x80','\x45','\x00','\x00', '\x90','\x47','\x78','\x3c','\x80','\x47','\x00','\x00','\x90','\x48','\x78','\x3c', '\x80','\x48','\x00','\x00','\xff','\x2f','\x00' }; const int FileTest1::test_mid_len = sizeof(test_mid); //175; const QString FileTest1::COPYRIGHT = u8"Copyright (C) 2006-2023 Pedro López-Cabanillas"; const QByteArray FileTest1::GSRESET = QByteArrayLiteral( "f04110421240007f0041f7" ); const QList FileTest1::NOTES = { 60, 62, 64, 65, 67, 69, 71, 72 }; const int FileTest1::FORMAT = 0; const int FileTest1::TRACKS = 1; const int FileTest1::DIVISION = 120; const int FileTest1::TEMPO = 100; void FileTest1::errorHandler(const QString& errorStr) { m_lastError = errorStr; qWarning() << errorStr; } void FileTest1::trackHandler(int ) { int i; // Text event m_engine->writeMetaEvent(0, copyright_notice, COPYRIGHT); m_engine->writeBpmTempo(0, TEMPO); m_engine->writeTimeSignature(0, 3, 2, 36, 8); // ts = 3/4 m_engine->writeKeySignature(0, 2, major_mode); // D major (2 sharps) // system exclusive event QByteArray gsreset = QByteArray::fromHex( GSRESET ); m_engine->writeMidiEvent(0, system_exclusive, long(gsreset.size()), gsreset.data()); // some note events for(i = 0; i < NOTES.length(); ++i) { m_engine->writeMidiEvent(0, note_on, 0, NOTES[i], 120); m_engine->writeMidiEvent(60, note_off, 0, NOTES[i], 0); } // final event m_engine->writeMetaEvent(0, end_of_track); } void FileTest1::headerEvent(int format, int ntrks, int division) { m_header = QString("Format=%1, Tracks=%2, Division=%3").arg(format).arg(ntrks).arg(division); } void FileTest1::trackStartEvent() { m_currentTrack++; } void FileTest1::trackEndEvent() { m_trackEnd = QString("End: %1").arg(m_currentTrack); } void FileTest1::endOfTrackEvent() { m_endOfTrack++; } void FileTest1::noteOnEvent(int , int pitch, int ) { m_numNoteOn++; m_lastNoteOn = pitch; } void FileTest1::noteOffEvent(int , int pitch, int ) { m_numNoteOff++; m_lastNoteOff = pitch; } void FileTest1::keyPressEvent(int , int pitch, int ) { m_lastKeyPress = pitch; } void FileTest1::ctlChangeEvent(int , int ctl, int ) { m_lastCtl = ctl; } void FileTest1::pitchBendEvent(int , int value) { m_lastPitchBend = value; } void FileTest1::programEvent(int , int patch) { m_lastProgram = patch; } void FileTest1::chanPressEvent(int , int press) { m_lastChanPress = press; } void FileTest1::sysexEvent(const QByteArray& data) { m_lastSysex = data; } void FileTest1::textEvent(int , const QString& data) { m_lastTextEvent = data; } void FileTest1::timeSigEvent(int b0, int b1, int b2, int b3) { m_lastTimeSig = QString("%1, %2, %3, %4").arg(b0).arg(b1).arg(b2).arg(b3); } void FileTest1::keySigEvent(int b0, int b1) { m_lastKeySig = QString("%1, %2").arg(b0).arg(b1); } void FileTest1::tempoEvent(int tempo) { m_lastTempo = static_cast( 6e7 / tempo ); } void FileTest1::initTestCase() { m_testData = QByteArray::fromRawData(test_mid, test_mid_len); } void FileTest1::cleanupTestCase() { m_testData.clear(); } void FileTest1::testCaseWriteSmf() { QByteArray data; QDataStream stream(&data, QIODevice::ReadWrite); m_engine->setDivision(DIVISION); m_engine->setFileFormat(FORMAT); m_engine->setTracks(TRACKS); m_engine->writeToStream(&stream); if (!m_lastError.isEmpty()) { QFAIL(m_lastError.toLocal8Bit()); } QCOMPARE(data, m_testData); } void FileTest1::testCaseReadSmf() { QDataStream stream(&m_testData, QIODevice::ReadWrite); m_engine->readFromStream(&stream); if (!m_lastError.isEmpty()) { QFAIL(m_lastError.toLocal8Bit()); } QCOMPARE(m_engine->getFileFormat(), FORMAT); QCOMPARE(m_engine->getDivision(), DIVISION); QCOMPARE(m_engine->getTracks(), TRACKS); QCOMPARE(m_lastTempo, TEMPO); QCOMPARE(m_lastTextEvent, COPYRIGHT); QCOMPARE(m_lastSysex, QByteArray::fromHex(GSRESET)); QCOMPARE(m_numNoteOn, NOTES.length()); QCOMPARE(m_numNoteOff, NOTES.length()); QCOMPARE(m_lastNoteOn, NOTES.last()); QCOMPARE(m_lastNoteOff, NOTES.last()); QCOMPARE(m_currentTrack, TRACKS); QCOMPARE(m_endOfTrack, TRACKS); } QTEST_APPLESS_MAIN(FileTest1) #include "filetest1.moc" DISABLE_WARNING_POP drumstick-2.9.0/tests/fileTest1/fileTest1.pro0000644000175000017500000000112514541630232020116 0ustar pedropedroQT += testlib QT -= gui equals(QT_MAJOR_VERSION, 6) { QT += core5compat } TARGET = fileTest1 CONFIG += qt warn_on depend_includepath testcase CONFIG += c++11 cmdline TEMPLATE = app include (../../global.pri) SOURCES += filetest1.cpp DEFINES += SRCDIR=\\\"$$PWD/\\\" INCLUDEPATH += . ../../library/include DESTDIR = ../../build/bin static:DEFINES+=DRUMSTICK_STATIC macx:!static { QMAKE_LFLAGS += -F$$OUT_PWD/../../build/lib -L$$OUT_PWD/../../build/lib LIBS += -framework drumstick-file } else { LIBS += -L$$OUT_PWD/../../build/lib \ -l$$drumstickLib(drumstick-file) } drumstick-2.9.0/tests/fileTest1/CMakeLists.txt0000644000175000017500000000226514541630232020302 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] add_executable (fileTest1 filetest1.cpp) target_link_libraries (fileTest1 PRIVATE Drumstick::File Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Test ) if(QT_VERSION VERSION_GREATER_EQUAL 6.0.0) target_link_libraries(fileTest1 PRIVATE Qt6::Core5Compat) endif() add_test (fileTest1 ${PROJECT_BINARY_DIR}/bin/fileTest1) drumstick-2.9.0/tests/fileTest2/0000755000175000017500000000000014541630232015536 5ustar pedropedrodrumstick-2.9.0/tests/fileTest2/fileTest2.pro0000644000175000017500000000112414541630232020117 0ustar pedropedroQT += testlib QT -= gui equals(QT_MAJOR_VERSION, 6) { QT += core5compat } TARGET = fileTest2 CONFIG += qt warn_on depend_includepath testcase CONFIG += c++11 cmdline TEMPLATE = app include (../../global.pri) SOURCES += filetest2.cpp DEFINES += SRCDIR=\\\"$$PWD/\\\" INCLUDEPATH += . ../../library/include DESTDIR = ../../build/bin static:DEFINES+=DRUMSTICK_STATIC macx:!static { QMAKE_LFLAGS += -F$$OUT_PWD/../../build/lib -L$$OUT_PWD/../../build/lib LIBS += -framework drumstick-file } else { LIBS += -L$$OUT_PWD/../../build/lib \ -l$$drumstickLib(drumstick-file) } drumstick-2.9.0/tests/fileTest2/CMakeLists.txt0000644000175000017500000000226514541630232020303 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] add_executable (fileTest2 filetest2.cpp) target_link_libraries (fileTest2 PRIVATE Drumstick::File Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Test ) if(QT_VERSION VERSION_GREATER_EQUAL 6.0.0) target_link_libraries(fileTest2 PRIVATE Qt6::Core5Compat) endif() add_test (fileTest2 ${PROJECT_BINARY_DIR}/bin/fileTest2) drumstick-2.9.0/tests/fileTest2/filetest2.cpp0000644000175000017500000002124414541630232020146 0ustar pedropedro/* Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This file is part of the Drumstick project, see https://sf.net/p/drumstick This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include #include #include DISABLE_WARNING_PUSH DISABLE_WARNING_DEPRECATED_DECLARATIONS using namespace drumstick::File; class FileTest2 : public QObject { Q_OBJECT public: explicit FileTest2(QObject* parent = nullptr); static const char test_wrk[]; static const int test_wrk_len; public Q_SLOTS: void fileHeader(int verh, int verl); void trackHeader(const QString& name1, const QString& name2, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop); void timeBase(int timebase); void noteEvent(int track, long time, int chan, int pitch, int vol, int dur); void timeSigEvent(int bar, int num, int den); void keySigEvent(int bar, int alt); void tempoEvent(long time, int tempo); void errorHandler(const QString& errorStr); void newTrackHeader(const QString& name, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop); private Q_SLOTS: void initTestCase(); void cleanupTestCase(); void testCaseReadWrkFile(); private: QWrk *m_engine; int m_timeBase; int m_numNotes; int m_lastNote; int m_tracks; int m_lastKeySig; float m_lastTempo; QByteArray m_testData; QString m_fileVersion; QString m_lastTimeSig; QString m_lastError; }; FileTest2::FileTest2(QObject* parent): QObject(parent), m_engine(nullptr), m_timeBase(0), m_numNotes(0), m_lastNote(0), m_tracks(0), m_lastKeySig(0), m_lastTempo(0) { m_engine = new QWrk(this); m_engine->setTextCodec(QTextCodec::codecForName("UTF-8")); connect(m_engine, &QWrk::signalWRKError, this, &FileTest2::errorHandler); connect(m_engine, &QWrk::signalWRKHeader, this, &FileTest2::fileHeader); connect(m_engine, &QWrk::signalWRKTrack, this, &FileTest2::trackHeader); connect(m_engine, &QWrk::signalWRKTimeBase, this, &FileTest2::timeBase); connect(m_engine, &QWrk::signalWRKNote, this, &FileTest2::noteEvent); connect(m_engine, &QWrk::signalWRKTimeSig, this, &FileTest2::timeSigEvent); connect(m_engine, &QWrk::signalWRKKeySig, this, &FileTest2::keySigEvent); connect(m_engine, &QWrk::signalWRKTempo, this, &FileTest2::tempoEvent); connect(m_engine, &QWrk::signalWRKNewTrack, this, &FileTest2::newTrackHeader); } const char FileTest2::test_wrk[] = { '\x43','\x41','\x4b','\x45','\x57','\x41','\x4c','\x4b','\x1a','\x00','\x02','\x0a','\x02','\x00','\x00','\x00', '\xc0','\x00','\x03','\x59','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00', '\x03','\x00','\x00','\x00','\x00','\x05','\x00','\x00','\x00','\x01','\x01','\x00','\x01','\x00','\x03','\x00', '\x00','\x01','\x00','\x00','\x00','\x00','\x00','\x01','\x01','\x00','\xff','\xff','\x01','\x7b','\x00','\x00', '\x00','\x01','\x02','\x03','\x04','\x05','\x06','\x07','\x08','\x09','\x0a','\x0b','\x0c','\x0d','\x0e','\x0f', '\x01','\x01','\x20','\x40','\x80','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00', '\x40','\x02','\x00','\x00','\xfe','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00', '\x16','\x16','\x00','\x00','\x00','\x03','\x00','\x06','\x4d','\x43','\x49','\x43','\x6d','\x64','\x01','\x04', '\x57','\x61','\x76','\x65','\x02','\x04','\x54','\x65','\x78','\x74','\x03','\x0f','\x14','\x00','\x00','\x00', '\x01','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\xe0','\x2e','\x00','\x00','\x00','\x00', '\x00','\x00','\x00','\x00','\x05','\x0e','\x00','\x00','\x00','\x01','\x00','\x00','\x00','\x00','\x00','\x01', '\x00','\x04','\x02','\x00','\x00','\x00','\x00','\x17','\x07','\x00','\x00','\x00','\x01','\x00','\x01','\x00', '\x04','\x02','\x00','\x0b','\x06','\x00','\x00','\x00','\x1e','\x00','\x00','\x00','\x00','\x00','\x11','\x0e', '\x00','\x00','\x00','\x00','\x00','\x09','\x00','\x25','\x00','\x6e','\x00','\x01','\x00','\x00','\x00','\x00', '\x00','\x10','\x0c','\x00','\x00','\x00','\x02','\x00','\x00','\x09','\x00','\x7f','\xff','\xff','\x00','\x00', '\x00','\x00','\x01','\x09','\x00','\x00','\x00','\x00','\x00','\x00','\x00','\x09','\x00','\x7f','\x00','\x00', '\x02','\x2c','\x00','\x00','\x00','\x00','\x00','\x05','\x00','\x00','\x00','\x00','\x90','\x58','\x64','\xc0', '\x00','\x00','\x00','\x00','\x90','\x25','\x64','\xc0','\x00','\xc0','\x00','\x00','\x90','\x25','\x64','\xc0', '\x00','\x80','\x01','\x00','\x90','\x25','\x64','\xc0','\x00','\x40','\x02','\x00','\x90','\x25','\x64','\xc0', '\x00','\x13','\x06','\x00','\x00','\x00','\x00','\x00','\x7f','\x00','\x40','\x00','\x19','\x42','\x00','\x00', '\x00','\x10','\x00','\x00','\x00','\x07','\x3f','\x01','\x00','\x07','\x3f','\x02','\x00','\x07','\x3f','\x03', '\x00','\x07','\x3f','\x04','\x00','\x07','\x3f','\x05','\x00','\x07','\x3f','\x06','\x00','\x07','\x3f','\x07', '\x00','\x07','\x3f','\x08','\x00','\x07','\x3f','\x09','\x00','\x07','\x3f','\x0a','\x00','\x07','\x3f','\x0b', '\x00','\x07','\x3f','\x0c','\x00','\x07','\x3f','\x0d','\x00','\x07','\x3f','\x0e','\x00','\x07','\x3f','\x0f', '\x00','\x07','\x3f','\xff' }; const int FileTest2::test_wrk_len = sizeof(test_wrk); //388 void FileTest2::fileHeader(int verh, int verl) { m_fileVersion = QString("%1.%2").arg(verh).arg(verl); } void FileTest2::errorHandler(const QString& errorStr) { m_lastError = errorStr; qWarning() << errorStr; } void FileTest2::trackHeader( const QString& name1, const QString& name2, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop ) { Q_UNUSED(name1) Q_UNUSED(name2) Q_UNUSED(trackno) Q_UNUSED(channel) Q_UNUSED(pitch) Q_UNUSED(velocity) Q_UNUSED(port) Q_UNUSED(selected) Q_UNUSED(muted) Q_UNUSED(loop) m_tracks++; } void FileTest2::timeBase(int timebase) { m_timeBase = timebase; } void FileTest2::noteEvent(int track, long time, int chan, int pitch, int vol, int dur) { Q_UNUSED(track) Q_UNUSED(time) Q_UNUSED(chan) Q_UNUSED(vol) Q_UNUSED(dur) m_numNotes++; m_lastNote = pitch; } void FileTest2::timeSigEvent(int bar, int num, int den) { Q_UNUSED(bar) m_lastTimeSig = QString("%1/%2").arg(num).arg(den); } void FileTest2::keySigEvent(int bar, int alt) { Q_UNUSED(bar) m_lastKeySig = alt; } void FileTest2::tempoEvent(long time, int tempo) { Q_UNUSED(time) double bpm = tempo / 100.0; m_lastTempo = bpm; } void FileTest2::newTrackHeader( const QString& name, int trackno, int channel, int pitch, int velocity, int port, bool selected, bool muted, bool loop ) { Q_UNUSED(name) Q_UNUSED(trackno) Q_UNUSED(channel) Q_UNUSED(pitch) Q_UNUSED(velocity) Q_UNUSED(port) Q_UNUSED(selected) Q_UNUSED(muted) Q_UNUSED(loop) m_tracks++; } void FileTest2::initTestCase() { m_testData = QByteArray::fromRawData(test_wrk, test_wrk_len); } void FileTest2::cleanupTestCase() { m_testData.clear(); } void FileTest2::testCaseReadWrkFile() { QDataStream stream(&m_testData, QIODevice::ReadWrite); m_engine->readFromStream(&stream); if (!m_lastError.isEmpty()) { QFAIL(m_lastError.toLocal8Bit()); } QCOMPARE(m_fileVersion, QString("2.0")); QCOMPARE(m_timeBase, 192); QCOMPARE(m_tracks, 1); QCOMPARE(m_lastTempo, 120.0); QCOMPARE(m_lastTimeSig, QString("4/4")); QCOMPARE(m_lastKeySig, 0); QCOMPARE(m_numNotes, 5); QCOMPARE(m_lastNote, 37); } QTEST_APPLESS_MAIN(FileTest2) #include "filetest2.moc" DISABLE_WARNING_POP drumstick-2.9.0/tests/fileTest3/0000755000175000017500000000000014541630232015537 5ustar pedropedrodrumstick-2.9.0/tests/fileTest3/filetest3.cpp0000644000175000017500000002226514541630232020154 0ustar pedropedro/* Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This file is part of the Drumstick project, see https://sf.net/p/drumstick This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include #include #include #include DISABLE_WARNING_PUSH DISABLE_WARNING_DEPRECATED_DECLARATIONS using namespace drumstick::File; class FileTest3 : public QObject { Q_OBJECT public: explicit FileTest3(QObject* parent = nullptr); static const char test_rmi[]; static const int test_rmi_len; static const int FORMAT; static const int TRACKS; static const int DIVISION; static const int TEMPO; static const QList NOTES; public Q_SLOTS: void dataHandler(const QString& dataType, const QByteArray& data); void errorHandler(const QString& errorStr); void headerEvent(int format, int ntrks, int division); void trackStartEvent(); void trackEndEvent(); void endOfTrackEvent(); void noteOnEvent(int chan, int pitch, int vol); void noteOffEvent(int chan, int pitch, int vol); void keyPressEvent(int chan, int pitch, int press); void ctlChangeEvent(int chan, int ctl, int value); void pitchBendEvent(int chan, int value); void programEvent(int chan, int patch); void chanPressEvent(int chan, int press); void sysexEvent(const QByteArray& data); void textEvent(int typ, const QString& data); void timeSigEvent(int b0, int b1, int b2, int b3); void keySigEvent(int b0, int b1); void tempoEvent(int tempo); private Q_SLOTS: void testCaseReadRmidi(); void initTestCase(); void cleanupTestCase(); private: Rmidi *m_rmidi; QSmf *m_engine; int m_numNoteOn; int m_lastNoteOn; int m_numNoteOff; int m_lastNoteOff; int m_lastKeyPress; int m_currentTrack; int m_endOfTrack; int m_lastCtl; int m_lastProgram; int m_lastChanPress; int m_lastPitchBend; int m_lastTempo; int m_format; int m_ntrks; int m_division; QByteArray m_testData; QByteArray m_lastSysex; QString m_lastError; QString m_trackEnd; QString m_lastTextEvent; QString m_lastTimeSig; QString m_lastKeySig; }; FileTest3::FileTest3(QObject* parent): QObject(parent), m_rmidi(nullptr), m_engine(nullptr), m_numNoteOn(0), m_lastNoteOn(0), m_numNoteOff(0), m_lastNoteOff(0), m_lastKeyPress(0), m_currentTrack(0), m_endOfTrack(0), m_lastCtl(0), m_lastProgram(0), m_lastChanPress(0), m_lastPitchBend(0), m_lastTempo(0), m_format(0), m_ntrks(0), m_division(0) { m_rmidi = new Rmidi(this); connect(m_rmidi, &Rmidi::signalRiffData, this, &FileTest3::dataHandler); m_engine = new QSmf(this); m_engine->setTextCodec(QTextCodec::codecForName("UTF-8")); connect(m_engine, &QSmf::signalSMFError, this, &FileTest3::errorHandler); connect(m_engine, &QSmf::signalSMFHeader, this, &FileTest3::headerEvent); connect(m_engine, &QSmf::signalSMFTrackStart, this, &FileTest3::trackStartEvent); connect(m_engine, &QSmf::signalSMFTrackEnd, this, &FileTest3::trackEndEvent); connect(m_engine, &QSmf::signalSMFNoteOn, this, &FileTest3::noteOnEvent); connect(m_engine, &QSmf::signalSMFNoteOff, this, &FileTest3::noteOffEvent); connect(m_engine, &QSmf::signalSMFKeyPress, this, &FileTest3::keyPressEvent); connect(m_engine, &QSmf::signalSMFCtlChange, this, &FileTest3::ctlChangeEvent); connect(m_engine, &QSmf::signalSMFPitchBend, this, &FileTest3::pitchBendEvent); connect(m_engine, &QSmf::signalSMFProgram, this, &FileTest3::programEvent); connect(m_engine, &QSmf::signalSMFChanPress, this, &FileTest3::chanPressEvent); connect(m_engine, &QSmf::signalSMFSysex, this, &FileTest3::sysexEvent); connect(m_engine, &QSmf::signalSMFText, this, &FileTest3::textEvent); connect(m_engine, &QSmf::signalSMFendOfTrack, this, &FileTest3::endOfTrackEvent); connect(m_engine, &QSmf::signalSMFTimeSig, this, &FileTest3::timeSigEvent); connect(m_engine, &QSmf::signalSMFKeySig, this, &FileTest3::keySigEvent); connect(m_engine, &QSmf::signalSMFTempo, this, &FileTest3::tempoEvent); } const char FileTest3::test_rmi[] = { '\x52', '\x49', '\x46', '\x46', '\x7c', '\x00', '\x00', '\x00', '\x52', '\x4d', '\x49', '\x44', '\x64', '\x61', '\x74', '\x61', '\x70', '\x00', '\x00', '\x00', '\x4d', '\x54', '\x68', '\x64', '\x00', '\x00', '\x00', '\x06', '\x00', '\x00', '\x00', '\x01', '\x03', '\xc0', '\x4d', '\x54', '\x72', '\x6b', '\x00', '\x00', '\x00', '\x5a', '\x00', '\xff', '\x58', '\x04', '\x04', '\x02', '\x18', '\x08', '\x00', '\xff', '\x59', '\x02', '\x00', '\x00', '\x00', '\xff', '\x51', '\x03', '\x07', '\xa1', '\x20', '\x00', '\xb0', '\x07', '\x65', '\x00', '\x90', '\x3c', '\x64', '\x83', '\x60', '\x3c', '\x00', '\x00', '\x3e', '\x64', '\x83', '\x60', '\x3e', '\x00', '\x00', '\x40', '\x64', '\x83', '\x60', '\x40', '\x00', '\x00', '\x41', '\x64', '\x83', '\x60', '\x41', '\x00', '\x00', '\x43', '\x64', '\x83', '\x60', '\x43', '\x00', '\x00', '\x45', '\x64', '\x83', '\x60', '\x45', '\x00', '\x00', '\x47', '\x64', '\x83', '\x60', '\x47', '\x00', '\x00', '\x48', '\x64', '\x83', '\x60', '\x48', '\x00', '\x00', '\xb0', '\x07', '\x65', '\x00', '\xff', '\x2f', '\x00' }; const int FileTest3::test_rmi_len = sizeof(test_rmi); //132; const QList FileTest3::NOTES = { 60, 62, 64, 65, 67, 69, 71, 72 }; const int FileTest3::FORMAT = 0; const int FileTest3::TRACKS = 1; const int FileTest3::DIVISION = 960; const int FileTest3::TEMPO = 120; void FileTest3::dataHandler(const QString &dataType, const QByteArray &data) { if (dataType == "RMID") { QDataStream ds(data); m_engine->readFromStream(&ds); } } void FileTest3::errorHandler(const QString& errorStr) { m_lastError = errorStr; qWarning() << Q_FUNC_INFO << errorStr; } void FileTest3::headerEvent(int format, int ntrks, int division) { m_format = format; m_ntrks = ntrks; m_division = division; } void FileTest3::trackStartEvent() { m_currentTrack++; } void FileTest3::trackEndEvent() { m_trackEnd = QString("End: %1").arg(m_currentTrack); //qDebug() << Q_FUNC_INFO << m_trackEnd; } void FileTest3::endOfTrackEvent() { m_endOfTrack++; } void FileTest3::noteOnEvent(int , int pitch, int vel) { Q_UNUSED(vel) m_numNoteOn++; m_lastNoteOn = pitch; //qDebug() << Q_FUNC_INFO << pitch << vel; } void FileTest3::noteOffEvent(int , int pitch, int vel) { Q_UNUSED(vel) m_numNoteOff++; m_lastNoteOff = pitch; //qDebug() << Q_FUNC_INFO << pitch << vel; } void FileTest3::keyPressEvent(int , int pitch, int ) { m_lastKeyPress = pitch; } void FileTest3::ctlChangeEvent(int , int ctl, int ) { m_lastCtl = ctl; } void FileTest3::pitchBendEvent(int , int value) { m_lastPitchBend = value; } void FileTest3::programEvent(int , int patch) { m_lastProgram = patch; } void FileTest3::chanPressEvent(int , int press) { m_lastChanPress = press; } void FileTest3::sysexEvent(const QByteArray& data) { m_lastSysex = data; } void FileTest3::textEvent(int , const QString& data) { m_lastTextEvent = data; } void FileTest3::timeSigEvent(int b0, int b1, int b2, int b3) { m_lastTimeSig = QString("%1, %2, %3, %4").arg(b0).arg(b1).arg(b2).arg(b3); //qDebug() << Q_FUNC_INFO << m_lastTimeSig; } void FileTest3::keySigEvent(int b0, int b1) { m_lastKeySig = QString("%1, %2").arg(b0).arg(b1); //qDebug() << Q_FUNC_INFO << m_lastKeySig; } void FileTest3::tempoEvent(int tempo) { m_lastTempo = static_cast( 6e7 / tempo ); //qDebug() << Q_FUNC_INFO << m_lastTempo; } void FileTest3::initTestCase() { m_testData = QByteArray::fromRawData(test_rmi, test_rmi_len); } void FileTest3::cleanupTestCase() { m_testData.clear(); } void FileTest3::testCaseReadRmidi() { QDataStream stream(&m_testData, QIODevice::ReadOnly); m_rmidi->readFromStream(&stream); if (!m_lastError.isEmpty()) { QFAIL(m_lastError.toLocal8Bit()); } QCOMPARE(m_format, FORMAT); QCOMPARE(m_ntrks, TRACKS); QCOMPARE(m_division, DIVISION); QCOMPARE(m_engine->getFileFormat(), FORMAT); QCOMPARE(m_engine->getTracks(), TRACKS); QCOMPARE(m_engine->getDivision(), DIVISION); QCOMPARE(m_lastTempo, TEMPO); QCOMPARE(m_numNoteOn, NOTES.length() * 2); QCOMPARE(m_numNoteOff, 0); // no notes off QCOMPARE(m_lastNoteOn, NOTES.last()); QCOMPARE(m_lastNoteOff, 0); // no notes off QCOMPARE(m_currentTrack, TRACKS); QCOMPARE(m_endOfTrack, TRACKS); } QTEST_APPLESS_MAIN(FileTest3) #include "filetest3.moc" DISABLE_WARNING_POP drumstick-2.9.0/tests/fileTest3/fileTest3.pro0000644000175000017500000000112514541630232020122 0ustar pedropedroQT += testlib QT -= gui equals(QT_MAJOR_VERSION, 6) { QT += core5compat } TARGET = fileTest3 CONFIG += qt warn_on depend_includepath testcase CONFIG += c++11 cmdline TEMPLATE = app include (../../global.pri) SOURCES += filetest3.cpp DEFINES += SRCDIR=\\\"$$PWD/\\\" INCLUDEPATH += . ../../library/include DESTDIR = ../../build/bin static:DEFINES+=DRUMSTICK_STATIC macx:!static { QMAKE_LFLAGS += -F$$OUT_PWD/../../build/lib -L$$OUT_PWD/../../build/lib LIBS += -framework drumstick-file } else { LIBS += -L$$OUT_PWD/../../build/lib \ -l$$drumstickLib(drumstick-file) } drumstick-2.9.0/tests/fileTest3/CMakeLists.txt0000644000175000017500000000226514541630232020304 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] add_executable (fileTest3 filetest3.cpp) target_link_libraries (fileTest3 PRIVATE Drumstick::File Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Test ) if(QT_VERSION VERSION_GREATER_EQUAL 6.0.0) target_link_libraries(fileTest3 PRIVATE Qt6::Core5Compat) endif() add_test (fileTest3 ${PROJECT_BINARY_DIR}/bin/fileTest3) drumstick-2.9.0/tests/rtTest/0000755000175000017500000000000014541630232015162 5ustar pedropedrodrumstick-2.9.0/tests/rtTest/rtTest.pro0000644000175000017500000000343314541630232017174 0ustar pedropedroTEMPLATE = app TARGET = rtTest QT += testlib QT -= gui CONFIG += c++11 cmdline include (../../global.pri) SOURCES += rttest.cpp DEFINES += SRCDIR=\\\"$$PWD/\\\" INCLUDEPATH += . ../../library/include/ DESTDIR = ../../build/bin static { CONFIG += link_prl DEFINES += DRUMSTICK_STATIC DEFINES += NET_BACKEND LIBS += -L$$OUT_PWD/../../build/lib/drumstick/ LIBS += -ldrumstick-rt-net-in \ -ldrumstick-rt-net-out packagesExist(fluidsynth) { DEFINES += FLUIDSYNTH_BACKEND LIBS += -ldrumstick-rt-fluidsynth macx { QMAKE_LFLAGS += -F/Library/Frameworks LIBS += -framework FluidSynth } else { CONFIG += link_pkgconfig PKGCONFIG += fluidsynth } } linux { DEFINES += LINUX_BACKEND LIBS += -ldrumstick-rt-alsa-in \ -ldrumstick-rt-alsa-out \ -ldrumstick-rt-eassynth \ -lsonivox \ -ldrumstick-alsa \ -lasound } unix:!macx { DEFINES += OSS_BACKEND LIBS += -ldrumstick-rt-oss-in \ -ldrumstick-rt-oss-out } macx { DEFINES += MAC_BACKEND LIBS += -ldrumstick-rt-mac-in \ -ldrumstick-rt-mac-out \ -ldrumstick-rt-macsynth \ -framework CoreMIDI \ -framework CoreFoundation } win32 { DEFINES += WIN_BACKEND LIBS += -ldrumstick-rt-win-in \ -ldrumstick-rt-win-out \ -lwinmm } } macx:!static { QMAKE_LFLAGS += -F$$OUT_PWD/../../build/lib -L$$OUT_PWD/../../build/lib LIBS += -framework drumstick-rt } else { LIBS += -L$$OUT_PWD/../../build/lib \ -l$$drumstickLib(drumstick-rt) } drumstick-2.9.0/tests/rtTest/rttest.cpp0000644000175000017500000000767214541630232017227 0ustar pedropedro/* Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This file is part of the Drumstick project, see https://sf.net/p/drumstick This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include #include #include #if defined(LINUX_BACKEND) Q_IMPORT_PLUGIN(ALSAMIDIInput) Q_IMPORT_PLUGIN(ALSAMIDIOutput) Q_IMPORT_PLUGIN(SynthController) #endif #if defined(MAC_BACKEND) Q_IMPORT_PLUGIN(MacMIDIInput) Q_IMPORT_PLUGIN(MacMIDIOutput) Q_IMPORT_PLUGIN(MacSynthOutput) #endif #if defined(WIN_BACKEND) Q_IMPORT_PLUGIN(WinMIDIInput) Q_IMPORT_PLUGIN(WinMIDIOutput) #endif #if defined(NET_BACKEND) Q_IMPORT_PLUGIN(NetMIDIInput) Q_IMPORT_PLUGIN(NetMIDIOutput) #endif #if defined(DUMMY_BACKEND) Q_IMPORT_PLUGIN(DummyInput) Q_IMPORT_PLUGIN(DummyOutput) #endif #if defined(FLUIDSYNTH_BACKEND) Q_IMPORT_PLUGIN(FluidSynthOutput) #endif #if defined(OSS_BACKEND) Q_IMPORT_PLUGIN(OSSInput) Q_IMPORT_PLUGIN(OSSOutput) #endif using namespace drumstick::rt; class RtTest : public QObject { Q_OBJECT public: RtTest(); private: QString joinConns(QList conns); private Q_SLOTS: void testRT(); }; RtTest::RtTest() = default; void RtTest::testRT() { QSettings settings; QList inputsList; QList outputsList; BackendManager man; man.refresh(&settings); QStringList paths = man.defaultPaths(); #if !defined(DRUMSTICK_STATIC) QVERIFY2(paths.length() > 0, "Plugins path is empty"); foreach(const QString& p, paths) { qDebug() << "path:" << p; } #endif inputsList = man.availableInputs(); QVERIFY2(inputsList.length() > 0, "There aren't input backends"); foreach(MIDIInput* input, inputsList) { QList conns = input->connections(); qDebug() << "input:" << input->backendName() << input->publicName(); qDebug() << " connections:" << (conns.isEmpty() ? "none" : joinConns(conns)); QCOMPARE(input->backendName().isEmpty(), false ); QCOMPARE(input->publicName().isEmpty(), false ); /*QVERIFY2(conns.length() > 0, "Backend without any connection"); QStringList avconns = input->connections(true); QVERIFY2(avconns.length() > 0, "Backend without any advanced connection"); QVERIFY2(avconns.length() >= conns.length(), "unexpected connections number");*/ } outputsList = man.availableOutputs(); QVERIFY2(outputsList.length() > 0, "There aren't output backends"); foreach(MIDIOutput* output, outputsList) { QList conns = output->connections(); qDebug() << "output:" << output->backendName() << output->publicName(); qDebug() << " connections:" << (conns.isEmpty() ? "none" : joinConns(conns)); /*QVERIFY2(conns.length() > 0, "Backend without any connection"); QStringList avconns = output->connections(true); QVERIFY2(avconns.length() > 0, "Backend without any advanced connection"); QVERIFY2(avconns.length() >= conns.length(), "unexpected connections number");*/ } } QString RtTest::joinConns(QList conns) { QString res; for(const MIDIConnection& c : conns) { res += c.first + ", "; } return res; } QTEST_GUILESS_MAIN(RtTest) #include "rttest.moc" drumstick-2.9.0/tests/rtTest/CMakeLists.txt0000644000175000017500000000564514541630232017734 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] set ( SOURCES rttest.cpp ) add_executable ( rtTest ${SOURCES} ) target_include_directories (rtTest PUBLIC ${CMAKE_SOURCE_DIR}/library/include ) target_link_libraries (rtTest PRIVATE Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Test Drumstick::RT ) if ((EXISTS "/dev/snd/") AND (EXISTS "/dev/snd/seq")) add_test (rtTest ${PROJECT_BINARY_DIR}/bin/rtTest) endif() if(STATIC_DRUMSTICK) if (FALSE) target_compile_definitions(rtTest PUBLIC DUMMY_BACKEND) target_link_libraries(rtTest PRIVATE drumstick-rt-dummy-in drumstick-rt-dummy-out) endif() if(ALSA_FOUND) target_compile_definitions(rtTest PUBLIC LINUX_BACKEND) target_link_libraries(rtTest PRIVATE drumstick-rt-alsa-in drumstick-rt-alsa-out drumstick-rt-eassynth) endif() if(UNIX AND NOT APPLE) target_compile_definitions(rtTest PUBLIC OSS_BACKEND) target_link_libraries(rtTest PRIVATE drumstick-rt-oss-in drumstick-rt-oss-out) endif() if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") target_compile_definitions(rtTest PUBLIC MAC_BACKEND) target_link_libraries(rtTest PRIVATE drumstick-rt-mac-in drumstick-rt-mac-out drumstick-rt-macsynth "-framework CoreMIDI -framework CoreFoundation") endif() if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") target_compile_definitions(rtTest PUBLIC WIN_BACKEND) target_link_libraries(rtTest PRIVATE drumstick-rt-win-in drumstick-rt-win-out winmm) endif() find_package(Qt${QT_VERSION_MAJOR}Network) if(Qt${QT_VERSION_MAJOR}Network_FOUND) target_compile_definitions(rtTest PUBLIC NET_BACKEND) target_link_libraries(rtTest PRIVATE drumstick-rt-net-in drumstick-rt-net-out) endif() if(FLUIDSYNTH_FOUND) target_compile_definitions(rtTest PUBLIC FLUIDSYNTH_BACKEND) target_link_libraries(rtTest PRIVATE drumstick-rt-fluidsynth) endif() endif() drumstick-2.9.0/tests/widgetsTest/0000755000175000017500000000000014541630232016203 5ustar pedropedrodrumstick-2.9.0/tests/widgetsTest/widgetsTest.pro0000644000175000017500000000104614541630232021234 0ustar pedropedroTEMPLATE = app TARGET = widgetsTest QT += testlib gui widgets CONFIG += c++11 cmdline include (../../global.pri) SOURCES += \ widgetstest.cpp DEFINES += SRCDIR=\\\"$$PWD/\\\" INCLUDEPATH += . ../../library/include/ DESTDIR = ../../build/bin macx:!static { QMAKE_LFLAGS += -F$$OUT_PWD/../../build/lib -L$$OUT_PWD/../../build/lib LIBS += -framework drumstick-rt drumstick-widgets } else { LIBS += -L$$OUT_PWD/../../build/lib \ -l$$drumstickLib(drumstick-rt) \ -l$$drumstickLib(drumstick-widgets) } drumstick-2.9.0/tests/widgetsTest/widgetstest.cpp0000644000175000017500000001077114541630232021263 0ustar pedropedro/* Copyright (C) 2008-2023, Pedro Lopez-Cabanillas This file is part of the Drumstick project, see https://sf.net/p/drumstick This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; If not, see . */ #include #include #include #include #include #include using namespace drumstick::widgets; class WidgetsTest : public QObject { Q_OBJECT public: WidgetsTest(); private Q_SLOTS: void testPaletteSingle(); void testPaletteDouble(); void testPaletteChannels(); void testPaletteScale(); void testPaletteKeys(); void testPaletteFont(); void testPaletteAssign(); void testPaletteUnchanged(); void testPaletteChanged(); private: QList m_paletteList { PianoPalette(PAL_SINGLE), PianoPalette(PAL_DOUBLE), PianoPalette(PAL_CHANNELS), PianoPalette(PAL_SCALE), PianoPalette(PAL_KEYS), PianoPalette(PAL_FONT), }; }; WidgetsTest::WidgetsTest() = default; //{ // qDebug() << "platform:" << qApp->platformName(); // for( PianoPalette& pal : m_paletteList) { // ... pal.paletteId(); // } //} void WidgetsTest::testPaletteSingle() { PianoPalette p(PAL_SINGLE); QCOMPARE(p, m_paletteList[PAL_SINGLE]); QCOMPARE(p.paletteId(), static_cast(PAL_SINGLE)); QCOMPARE(p.getNumColors(), 1); QCOMPARE(p.isHighLight(), true); QCOMPARE(p.isBackground(), false); QCOMPARE(p.isForeground(), false); } void WidgetsTest::testPaletteDouble() { PianoPalette p(PAL_DOUBLE); QCOMPARE(p, m_paletteList[PAL_DOUBLE]); QCOMPARE(p.paletteId(), static_cast(PAL_DOUBLE)); QCOMPARE(p.getNumColors(), 2); QCOMPARE(p.isHighLight(), true); QCOMPARE(p.isBackground(), false); QCOMPARE(p.isForeground(), false); } void WidgetsTest::testPaletteChannels() { PianoPalette p(PAL_CHANNELS); QCOMPARE(p, m_paletteList[PAL_CHANNELS]); QCOMPARE(p.paletteId(), static_cast(PAL_CHANNELS)); QCOMPARE(p.getNumColors(), 16); QCOMPARE(p.isHighLight(), true); QCOMPARE(p.isBackground(), false); QCOMPARE(p.isForeground(), false); } void WidgetsTest::testPaletteScale() { PianoPalette p(PAL_SCALE); QCOMPARE(p, m_paletteList[PAL_SCALE]); QCOMPARE(p.paletteId(), static_cast(PAL_SCALE)); QCOMPARE(p.getNumColors(), 12); QCOMPARE(p.isHighLight(), false); QCOMPARE(p.isBackground(), true); QCOMPARE(p.isForeground(), false); } void WidgetsTest::testPaletteKeys() { PianoPalette p(PAL_KEYS); QCOMPARE(p, m_paletteList[PAL_KEYS]); QCOMPARE(p.paletteId(), static_cast(PAL_KEYS)); QCOMPARE(p.getNumColors(), 2); QCOMPARE(p.isHighLight(), false); QCOMPARE(p.isBackground(), true); QCOMPARE(p.isForeground(), false); } void WidgetsTest::testPaletteFont() { PianoPalette p(PAL_FONT); QCOMPARE(p, m_paletteList[PAL_FONT]); QCOMPARE(p.paletteId(), static_cast(PAL_FONT)); QCOMPARE(p.getNumColors(), 4); QCOMPARE(p.isHighLight(), false); QCOMPARE(p.isBackground(), false); QCOMPARE(p.isForeground(), true); } void WidgetsTest::testPaletteAssign() { PianoPalette a(PAL_SINGLE); PianoPalette b(PAL_DOUBLE); a = b; QCOMPARE(a, b); QCOMPARE(a, m_paletteList[PAL_DOUBLE]); QCOMPARE(a.paletteId(), b.paletteId()); QCOMPARE(a.getNumColors(), b.getNumColors()); } void WidgetsTest::testPaletteUnchanged() { PianoPalette p(PAL_KEYS); p.setColor(0, Qt::white); p.setColor(1, Qt::black); QCOMPARE(p, m_paletteList[PAL_KEYS]); QCOMPARE(p.paletteId(), static_cast(PAL_KEYS)); QCOMPARE(p.getNumColors(), 2); } void WidgetsTest::testPaletteChanged() { PianoPalette p(PAL_KEYS); p.setColor(0, Qt::black); p.setColor(1, Qt::white); QVERIFY(p != m_paletteList[PAL_KEYS]); p.resetColors(); QCOMPARE(p, m_paletteList[PAL_KEYS]); } QTEST_MAIN(WidgetsTest) #include "widgetstest.moc" drumstick-2.9.0/tests/widgetsTest/CMakeLists.txt0000644000175000017500000000252714541630232020751 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] find_package(Qt${QT_VERSION_MAJOR}Widgets REQUIRED) set ( SOURCES widgetstest.cpp ) add_executable ( widgetsTest ${SOURCES} ) target_include_directories ( widgetsTest PUBLIC ${CMAKE_SOURCE_DIR}/library/include ) target_link_libraries (widgetsTest PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Test Drumstick::Widgets ) add_test (widgetsTest ${PROJECT_BINARY_DIR}/bin/widgetsTest) set_tests_properties(widgetsTest PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen") drumstick-2.9.0/tests/tests.pro0000644000175000017500000000030514541630232015557 0ustar pedropedroTEMPLATE = subdirs SUBDIRS += fileTest1 \ fileTest2 \ fileTest3 \ rtTest \ widgetsTest linux { SUBDIRS += \ alsaTest1 \ alsaTest2 } drumstick-2.9.0/tests/CMakeLists.txt0000644000175000017500000000242614541630232016441 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] find_package(Qt${QT_VERSION_MAJOR}Test REQUIRED) set (CMAKE_AUTOMOC ON) enable_testing() if (BUILD_ALSA AND ALSA_FOUND) add_subdirectory(alsaTest1) add_subdirectory(alsaTest2) endif() if (BUILD_FILE) add_subdirectory(fileTest1) add_subdirectory(fileTest2) add_subdirectory(fileTest3) endif() if (BUILD_RT) add_subdirectory(rtTest) if(BUILD_WIDGETS) add_subdirectory(widgetsTest) endif() endif() drumstick-2.9.0/CMakeLists.txt0000644000175000017500000003254014541630232015277 0ustar pedropedro#[===========================================================================[ MIDI C++ Library Copyright (C) 2005-2023 Pedro Lopez-Cabanillas This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . #]===========================================================================] cmake_minimum_required(VERSION 3.16) project( Drumstick VERSION 2.9.0 LANGUAGES CXX DESCRIPTION "MIDI C++ Libraries for Qt" HOMEPAGE_URL "https://drumstick.sourceforge.io" ) list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_admin) include(SCMRevision) include(CTest) include(GNUInstallDirs) set(RELEASE_DATE "December 23, 2023") set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # cmake bug https://gitlab.kitware.com/cmake/cmake/issues/18396 closed in cmake 3.14 if(APPLE AND ${CMAKE_VERSION} VERSION_LESS 3.14) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") endif() if (WIN32 AND CMAKE_CXX_COMPILER_ID MATCHES "MSVC") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8") #/source-charset:utf-8 /execution-charset:utf-8 endif() #[==[ compiler and linker extra checks: if(UNIX AND NOT APPLE) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -pedantic") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--as-needed -Wl,-z,defs") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed -Wl,-z,defs") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed -Wl,--no-undefined -Wl,-z,defs") endif() endif() #]==] add_definitions(-DVERSION=${PROJECT_VERSION}) if (DEFINED PROJECT_WC_REVISION) add_definitions(-DREVISION=${PROJECT_WC_REVISION}) endif() if (UNIX) set(_DOCS_INIT ON) else() set(_DOCS_INIT OFF) endif() set(_DBUS_INIT OFF) if (UNIX AND NOT APPLE) set(_PULSE_INIT ON) else () set(_PULSE_INIT OFF) endif () if (APPLE) set(_FRAMEWORKS_INIT ON) else() set(_FRAMEWORKS_INIT OFF) endif() # ALSA Sequencer is only available on Linux if (${CMAKE_SYSTEM} MATCHES "Linux") set(_BUILD_ALSA_INIT ON) else() set(_BUILD_ALSA_INIT OFF) endif() option(BUILD_DOCS "Build Doxygen documentation and man pages" ${_DOCS_INIT}) option(BUILD_UTILS "Build Utilities and Examples" ON) option(BUILD_FRAMEWORKS "Build macOS style frameworks" ${_FRAMEWORKS_INIT}) option(BUILD_ALSA "Build the libdrumstick-alsa library (Linux only)" ${_BUILD_ALSA_INIT}) option(BUILD_FILE "Build the libdrumstick-file library" ON) option(BUILD_RT "Build the libdrumstick-rt library" ON) option(BUILD_WIDGETS "Build the libdrumstick-widgets library" ON) option(STATIC_DRUMSTICK "Build and link static libraries instead of shared" OFF) option(USE_DBUS "Include DBus support (required for RealtimeKit)" ${_DBUS_INIT}) option(USE_PULSEAUDIO "Build Sonivox RT backend (if PulseAudio is available)" ${_PULSE_INIT}) option(USE_FLUIDSYNTH "Build FluidSynth RT backend (if available)" ON) option(USE_NETWORK "Build Network RT backend (if QtNetwork is available)" ON) option(USE_SONIVOX "Build Sonivox RT backend (if Sonivox is available)" ${_PULSE_INIT}) option(USE_QT5 "Prefer building with Qt5 instead of Qt6") if (BUILD_WIDGETS AND NOT BUILD_RT) message(FATAL_ERROR "libdrumstick-widgets requires libdrumstick-rt") endif() if(STATIC_DRUMSTICK) set(BUILD_SHARED_LIBS OFF) add_definitions(-DDRUMSTICK_STATIC) message(STATUS "Building static libraries") else() set(BUILD_SHARED_LIBS ON) if (BUILD_FRAMEWORKS) message(STATUS "Building macOS style frameworks") else() message(STATUS "Building unix dynamic libraries") endif() endif() if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.15) set(CMAKE_FRAMEWORK ${BUILD_FRAMEWORKS}) endif() if (UNIX AND NOT APPLE) if ((USE_PULSEAUDIO AND NOT USE_SONIVOX) OR (USE_SONIVOX AND NOT USE_PULSEAUDIO)) message(FATAL_ERROR "Wrong combination of USE_SONIVOX and USE_PULSEAUDIO options") endif() endif() if (USE_QT5) find_package(QT NAMES Qt5 REQUIRED) else() find_package(QT NAMES Qt6 REQUIRED) endif() if (QT_VERSION VERSION_LESS 6.0.0) find_package(Qt5 5.9 COMPONENTS Core REQUIRED) else() if (QT_VERSION VERSION_LESS 6.2.0) message (FATAL_ERROR "Qt6 >= 6.2 is required, or use Qt5 >= 5.9") endif() find_package(Qt6 6.2 COMPONENTS Core REQUIRED) if (BUILD_FILE) find_package(Qt6Core5Compat REQUIRED) endif() endif() message(STATUS "Found Qt version = ${QT_VERSION}") set(HAVE_DBUS OFF) if(BUILD_ALSA AND USE_DBUS) find_package(Qt${QT_VERSION_MAJOR}DBus REQUIRED) if (${Qt${QT_VERSION_MAJOR}DBus_FOUND}) set(HAVE_DBUS ON) message(STATUS "Found QtDBus version = ${Qt${QT_VERSION_MAJOR}DBus_VERSION}") endif() endif() set(HAVE_NETWORK OFF) if(BUILD_RT AND USE_NETWORK) find_package(Qt${QT_VERSION_MAJOR}Network REQUIRED) if(Qt${QT_VERSION_MAJOR}Network_FOUND) message(STATUS "Found QtNetwork version = ${Qt${QT_VERSION_MAJOR}Network_VERSION}") set(HAVE_NETWORK ON) else() message(FATAL_ERROR "QtNetwork library not available. Network RT backend can't be built.") endif() endif() find_package(PkgConfig REQUIRED) if(PKG_CONFIG_FOUND) message(STATUS "Program pkg-config ${PKG_CONFIG_VERSION_STRING} found (${PKG_CONFIG_EXECUTABLE})") else() message(FATAL_ERROR "Program pkg-config not found") endif() # ALSA Sequencer is only available on Linux if (BUILD_ALSA AND (${CMAKE_SYSTEM} MATCHES "Linux")) pkg_check_modules(ALSA REQUIRED IMPORTED_TARGET alsa>=1.0.0) if(ALSA_FOUND) message(STATUS "Found ALSA version = ${ALSA_VERSION}") else() message(FATAL_ERROR "library libdrumstick-alsa requested, but ALSA Sequencer was not found") endif() endif() set(HAVE_PULSEAUDIO OFF) if(BUILD_RT AND PKG_CONFIG_FOUND AND UNIX AND NOT APPLE) if (USE_PULSEAUDIO) pkg_check_modules(PULSE QUIET IMPORTED_TARGET libpulse-simple) if(PULSE_FOUND) message(STATUS "Found PulseAudio version = ${PULSE_VERSION}") set(HAVE_PULSEAUDIO ON) else() message(FATAL_ERROR "PulseAudio library not available. Sonivox RT backend can't be built.") endif() endif() endif() set(HAVE_FLUIDSYNTH OFF) if (BUILD_RT AND PKG_CONFIG_FOUND) if (USE_FLUIDSYNTH) pkg_check_modules(FLUIDSYNTH QUIET IMPORTED_TARGET fluidsynth>=1.1.1) if(FLUIDSYNTH_FOUND) message(STATUS "Found FluidSynth version = ${FLUIDSYNTH_VERSION}") set(HAVE_FLUIDSYNTH ON) else() message(FATAL_ERROR "FluidSynth library not available. FluidSynth RT backend can't be built.") endif() endif() endif() set(HAVE_SONIVOX OFF) if (BUILD_RT AND USE_SONIVOX) find_package(sonivox CONFIG QUIET) if(sonivox_FOUND) message(STATUS "Found Sonivox version = ${sonivox_VERSION}") set(HAVE_SONIVOX ON) else() message(FATAL_ERROR "Sonivox library not available. SonivoxEas Synth RT backend can't be built.") endif() endif() if (${CMAKE_SYSTEM} MATCHES "Darwin" AND QT_VERSION VERSION_LESS 6.0.0) find_package(Qt${QT_VERSION_MAJOR}Concurrent REQUIRED) if (Qt${QT_VERSION_MAJOR}Concurrent_FOUND) message(STATUS "Found Qt${QT_VERSION_MAJOR}Concurrent version = ${Qt${QT_VERSION_MAJOR}Concurrent_VERSION_STRING}") endif() endif() # CMAKE_SYSTEM_PROCESSOR is broken on Windows with MSVC # cmake bug https://gitlab.kitware.com/cmake/cmake/-/issues/15170 still open in 2023/06 if (MSVC) string(TOLOWER ${MSVC_CXX_ARCHITECTURE_ID} CMAKE_SYSTEM_PROCESSOR) endif() message (STATUS "${PROJECT_NAME} v${PROJECT_VERSION} CMake version: ${CMAKE_VERSION} Install prefix: ${CMAKE_INSTALL_PREFIX} Build configuration: ${CMAKE_BUILD_TYPE} System: ${CMAKE_SYSTEM_NAME} Processor: ${CMAKE_SYSTEM_PROCESSOR} Qt Version: ${QT_VERSION} D-Bus support: ${HAVE_DBUS} Network support: ${HAVE_NETWORK} PulseAudio support: ${HAVE_PULSEAUDIO} FluidSynth support: ${HAVE_FLUIDSYNTH} Sonivox support: ${HAVE_SONIVOX} Building libdrumstick-alsa: ${BUILD_ALSA} Building libdrumstick-file: ${BUILD_FILE} Building libdrumstick-rt: ${BUILD_RT} Building libdrumstick-widgets: ${BUILD_WIDGETS} Building tests: ${BUILD_TESTING} Building docs: ${BUILD_DOCS} Building utils: ${BUILD_UTILS} Building frameworks: ${BUILD_FRAMEWORKS}" ) add_subdirectory(library) if (BUILD_UTILS) add_subdirectory(utils) endif() add_subdirectory(icons) if(BUILD_TESTING) add_subdirectory(tests) endif() # translations add_custom_target( update-translations COMMENT "Updating translations" ) if (BUILD_WIDGETS) add_dependencies( update-translations update-widget-translations ) endif() if (BUILD_UTILS) if (BUILD_RT AND BUILD_WIDGETS) add_dependencies( update-translations update-vpiano-translations ) endif() if(BUILD_ALSA AND ALSA_FOUND) add_dependencies( update-translations update-drumgrid-translations ) if(BUILD_FILE) add_dependencies( update-translations update-guiplayer-translations ) endif() endif() endif() # pkg-config support if ( IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}" ) set ( pkgconfig_libdir "${CMAKE_INSTALL_LIBDIR}" ) else () set ( pkgconfig_libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}" ) endif () if ( IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}" ) set ( pkgconfig_includedir "${CMAKE_INSTALL_INCLUDEDIR}" ) else () set ( pkgconfig_includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}" ) endif () if(UNIX AND NOT APPLE) if (BUILD_DOCS) find_package(Doxygen REQUIRED dot) if(DOXYGEN_FOUND) string(REPLACE ";" " " DOXYGEN_INCLUDE_DIRS "${Qt${QT_VERSION_MAJOR}Core_INCLUDE_DIRS}") configure_file("${CMAKE_SOURCE_DIR}/Doxyfile.in" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" IMMEDIATE @ONLY) add_custom_target(doxygen ${DOXYGEN_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) endif() add_subdirectory(doc) endif() configure_file(drumstick.spec.in drumstick.spec IMMEDIATE @ONLY) if(ALSA_FOUND AND PKG_CONFIG_FOUND) configure_file(drumstick-alsa.pc.in drumstick-alsa.pc IMMEDIATE @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/drumstick-alsa.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) endif() # XML mime types set( SHARED_MIME_INFO_MINIMUM_VERSION "0.30" ) set( XDG_MIME_INSTALL_DIR "${CMAKE_INSTALL_DATAROOTDIR}/mime/packages" ) find_package( SharedMimeInfo ) if( SHARED_MIME_INFO_FOUND ) install( FILES drumstick.xml DESTINATION ${XDG_MIME_INSTALL_DIR} ) update_xdg_mimetypes( ${XDG_MIME_INSTALL_DIR} ) endif() endif() if(PKG_CONFIG_FOUND) configure_file(drumstick-file.pc.in drumstick-file.pc IMMEDIATE @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/drumstick-file.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) configure_file(drumstick-rt.pc.in drumstick-rt.pc IMMEDIATE @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/drumstick-rt.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) configure_file(drumstick-widgets.pc.in drumstick-widgets.pc IMMEDIATE @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/drumstick-widgets.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) endif() # uninstall custom target configure_file( "${CMAKE_SOURCE_DIR}/cmake_admin/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") if(UNIX) # tarball target add_custom_target( tarball COMMAND mkdir -p drumstick-${PROJECT_VERSION} COMMAND cp -r cmake_admin drumstick-${PROJECT_VERSION} COMMAND cp -r library drumstick-${PROJECT_VERSION} COMMAND cp -r utils drumstick-${PROJECT_VERSION} COMMAND cp -r doc drumstick-${PROJECT_VERSION} COMMAND cp -r icons drumstick-${PROJECT_VERSION} COMMAND cp -r tests drumstick-${PROJECT_VERSION} COMMAND cp CMakeLists.txt AUTHORS COPYING ChangeLog NEWS TODO *.md drumstick*.in drumstick.pro drumstick.xml configure* Doxyfile.in global.pri versioninfo.rc.in drumstick-${PROJECT_VERSION} COMMAND tar -cj --exclude=.[a-z]* -f drumstick-${PROJECT_VERSION}.tar.bz2 drumstick-${PROJECT_VERSION} COMMAND tar -cz --exclude=.[a-z]* -f drumstick-${PROJECT_VERSION}.tar.gz drumstick-${PROJECT_VERSION} COMMAND zip -qr drumstick-${PROJECT_VERSION}.zip drumstick-${PROJECT_VERSION} -x '.[a-z]*' COMMAND rm -rf drumstick-${PROJECT_VERSION} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) endif() drumstick-2.9.0/AUTHORS0000644000175000017500000000036114541630232013603 0ustar pedropedroPedro Lopez-Cabanillas Translators: Frank Kober - German and French Pavel Fric - Czech Sergey Basalaev - Russian Pedro Lopez-Cabanillas - Spanish Giovanni Mariani - Italian Hycinth - Chinese drumstick-2.9.0/COPYING0000644000175000017500000010451514541630232013574 0ustar pedropedro GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . drumstick-2.9.0/ChangeLog0000644000175000017500000004776214541630232014325 0ustar pedropedro2023-12-23 * Documentation updated * Release 2.9.0 2023-12-08 * fix for GH #15: new SettingsFactory public static methods: fileName() and format(). * fix for GH #16: new slot writeSettings() for some backends: Sonivox, FluidSynth, Network. 2023-12-06 * Preparations for release 2.9.0: Building with Qt6 by default. For Qt5 compatibility, a new option USE_QT5 must be set. Upgraded compiler/runtime requirements to C++17. Upgraded CMake requirement to 3.16 2023-08-25 * Release 2.8.1 2023-08-24 * Chinese translation updated. Thanks to Hycinth 2023-08-17 * CMake config check to avoid mixing Qt6 with Qt5 2023-07-31 * Fix for GH ticket #13: errors while building drumstick 2.8.0 2023-07-01 * Release 2.8.0 2023-06-25 * Widgets: fixed compiler definitions, removed object headers duplicity * dumprmi utility: fixed QScopedPointer template for arrays * Updated doxygen settings for Doxygen 1.9.5 2023-06-24 * Widgets: clip note children (labels) 2023-06-18 * Unit tests: conditionally run the ALSA test and run the Widgets test using the QPA platform "offscreen" 2023-06-17 * Fix for ticket #49: Link failure when Qt6 is compiled with visibility protected * ALSA: added assignment operators for sysex and text events 2023-06-11 * Fix for ticket #48: SMF Parser does not report enough errors 2023-05-07 * Implemented change soundfont function for the SonivoxEAS backend. 2023-05-06 * RT Sonivox backend: enabled DLS file loading * Widgets: Sonivox configuration dialog updated 2022-12-18 * File: Improved RMID file support * Extract DLS embedded data * Decode DLS instrument names * New mime type 2022-10-1 * Release 2.7.2 2022-09-08 * Italian translations updated. Thanks to Giovanni Mariani. 2022-08-29 * fix for ticket #46: FluidSynth backend should honor its default soundfont. 2022-08-14 * Release 2.7.1 2022-08-13 * Fix for ticket #45: sonivox library dependency is optional, and easily forgotten * Fixed underlinking in some RT backends 2022-07-31 * Release 2.7.0 2022-07-28 * ticket #44: removed sonivox library sources from the source tree * new option USE_SONIVOX to let the user decide to build the sonivox backend or not * using find_package(sonivox) to integrate the external sonivox library as a dependency 2022-07-21 * fluidsynth: default windows audio driver is now wasapi. Fixed pulseaudio settings. 2022-07-19 * fluidsynth backend files and classes renamed with more specific names. * vpiano error message citing the DRUMSTICKRT environment variable. * some work to update the documentation components diagram (unfinished). 2022-07-17 * fixed fluidsynth backend to support channel pressure and key pressure MIDI events * revised backend and configuration dialog to reflect the changes in fluidsynth 2.2.8 2022-07-15 * ticket #43: Drumstick::RT dummy plugins now include its own configuration dialogs. * Drumstick::Widgets include support for these dialogs, implemented in the already * existing functions of 2022-07-14 * ticket #42: Drumstick::Widgets - fixed piano designer plugin to show more properties * properties added: labelFont and startKey * properties fixed: showLabels, alterations, labelOrientation, labelOctave 2022-06-30 * Drumstick::ALSA revision, registering SequencerEvent with qMetaType * Utils: dumpmid small revision 2022-06-13 * ticket #41 Widgets: Option to use subscript octave designation * Utils: vpiano uses the subscript octave designation option * version bumped to 2.7.0 for the next development cycle 2022-05-20 * Release 2.6.1 2022-05-19 * Another fix related to ticket #39: CMAKE_INSTALL_LIBDIR is passed to the compiler as a symbol named LIBSUFFIX (see library/rt/CMakeLists.txt:69). It needed to be tested at runtime with QDir::isAbsolutePath(). * Bumped version to 2.6.1 for the next release. 2022-05-17 * Fixed ticket #39: pkg-config files are broken when CMAKE_INSTALL_{INCLUDE,LIB}DIR is absolute 2022-04-22 * Using QGuiApplication::setDesktopFileName on utils to fix the wrong icon in KDE+Wayland 2022-04-04 * Release 2.6.0 2022-04-01 * Utils: vpiano note labels font stability fix. 2022-03-31 * Widgets: pianokeybd has 2 new functions, setUsingNativeFilter() and isUsingNativeFilter(). * Bumped version to 2.6.0 for the next release. 2022-03-29 * Disabled runnig lupdate for every build 2022-03-28 * Widgets: fixed pianokeybd's touchscreen input on Linux, including X11 and Wayland. * Utils: vpiano new menu options to allow enabling keyboard, mouse and touchscreen input. 2022-03-22 * Widgets: pianokeybd internal raw computer keyboard input processing * Utils: vpiano gets and uses a new raw keyboard menu option * Bump version for the next development cycle 2022-02-07 * Release 2.5.1 2022-02-03 * Utils: fixed loading translations, program names and messages * cmake buildsystem: run qt::lupdate after building targets 2022-01-28 * Widgets: Fluidsynth soundfonts dialog, updated filters * RT: Fluidsynth backend initialization: failure report for invalid soundfonts 2022-01-27 * Avoid loading translations for English 2022-01-15 * AppStream MetaInfo added for the three GUI utilities 2021-12-16 * Bump version for the next development cycle 2021-12-12 * Fixed install: header macros.h missing when only BUILD_ALSA is selected. * Fixed linking tests when using qmake 2021-12-10 * Release 2.5.0 2021-12-07 * Documentation/deprecation of Drumstick::File functions affected by QTextCodec * Documented new build options and Qt6Core5Compat dependency for Drumstick::File * Raised macOS deployment target to 10.13 (High Sierra) 2021-12-03 * RT, VPiano: Fixed ALSA backend, enable empty input connection. * New build options: BUILD_ALSA, BUILD_FILE, BUILD_RT, BUILD_WIDGETS * Reduced usage of Qt6Core5Compat to the minimum 2021-11-08 * Widgets: changed the white keys background picture depending on the key background color * VPiano: new option to display inverted key colors 2021-10-28 * fix for ticket #37: WRK format markers are not decoded * bump version to 2.5.0 for the new development cycle 2021-10-24 * Widgets library Swedish translation updated. Thanks to Magnus Johansson. * Widgets library Czech translation updated. Thanks to Pavel Fric. * Release 2.4.1 2021-10-22 * New build option USE_QT to choose among Qt major versions (5 or 6). By default (if not set) it uses whatever is found. note: Qt6 support is still experimental. 2021-10-21 * fix for ticket #35: build with Qt 5.11 is possible again. 2021-10-20 * Vpiano: fix for a similar bug to vmpk ticket #74: crash in Linux. 2021-10-17 * Widgets: using buffer time in FluidSynth configuration dialog when the driver is pulseaudio. Default is 30 ms on both FluidSynth and Sonivox EAS. * RT FluidSynth backend: adjust-latency when using pulseaudio driver. 2021-10-10 * fixed wrong license in two documents (images): should be CC-BY-SA * removed obsolete images 2021-10-8 * revised defaults and ranges for the FluidSynth RT backend parameters, using the same values as the upstream library. * fixed validation of parameters in the FluidSynth configuration dialog. 2021-09-19 * Release 2.4.0 2021-09-11 * implementation of ticket #29: RIFF RMID file support * New utility: dumprmi 2021-08-20 * Enable by default the internal reverb on macOS DLS Synth * Avoid hardcoded font family name in vpiano 2021-08-19 * cmake buildsystem: macOS revision 2021-08-17 * widgets: new italian translation 2021-08-02 * implementation for ticket #33: versioninfo object for windows libraries 2021-08-01 * bumped version to 2.4.0 for the next development cycle * exported targets cleanups * implemented ticket #32: missing library version functions in File and RT libs 2021-07-29 * Release 2.3.1 2021-07-28 * New option: BUILD_FRAMEWORKS for macOS style frameworks instead of plain Unix libraries 2021-07-22 * Fixed SMF system exclusive event write method 2021-07-14 * Fixed WRK file processing in guiplayer utility * updated documentation * bumped version to 2.3.1 for the next development cycle 2021-06-29 * Release 2.3.0 2021-06-23 * Widgets: Fixed touch events - checked pressure capability 2021-06-14 * Fixes after ticket #31 tests 2021-06-10 * implementation of ticket #31: fallback OUT drivers for Drumstick::RT two new methods in class BackendManager: MIDIInput* findInput(QString name); MIDIOutput* findOutput(QString name); They return the requested backend or another suitable replacement. * Bump version number to 2.3.0 2021-06-09 * New options: USE_PULSEAUDIO, USE_FLUIDSYNTH, USE_NETWORK * Revised CMake buildsystem and documents 2021-06-08 * Bump version number to 2.2.2 for the next development cycle * fix incomplete ALSA RT output plugin 2021-05-31 * Release 2.2.1 2021-05-30 * experimental cmake support for building with Qt6 2021-05-29 * fixed ticket #30: RT initialization diagnostics 2021-05-17 * bump version to 2.2.1 for the next development cycle 2021-05-09 * Release 2.2.0 2021-05-04 * French and German translations updated (Thanks to Frank Kober) 2021-04-28 * removed warnings when buiding with Qt >= 5.15 2021-04-25 * Standarization: MIDI texts/lyrics encoding defaults to Latin1 2021-04-21 * Drumstick::File * new QWrk class signals with a QByteArray parameter instead of QString: * signalWRKText2 * signalWRKTrack2 * signalWRKComments2 * signalWRKNewTrack2 * signalWRKTrackName2 * signalWRKStringTable2 * signalWRKSegment2 * signalWRKExpression2 * the old signals are still emitted when a QTextCodec is assigned 2021-04-06 * Drumstick::RT * FluidSynth backend: initialization moved to a background thread * retrieve dynamically the audio driver names for using in configuration dialog 2021-04-05 * Drumstick::Widgets * added wasapi option to fluidsynth settings dialog in Windows (WIP) * removed background settings from pianokeybd, to allow better dark theme transitions 2021-04-04 * new option: BUILD_UTILS (ON by default). * documentation for BUILD_UTILS and BUILD_TESTING options. 2021-04-01 * added SCM Revision to the about box of GUI utils 2021-03-31 * release 2.1.1 2021-03-28 * New build option: BUILD_DOCS (ON by default in Unix) 2021-03-21 * fix for ticket #28: highlight color is wrong unless velocity tint is active 2021-03-20 * release 2.1.0 2021-03-11 * Russian translation update. Thanks to Sergey Basalaev 2021-03-06 * Czech translation update. Thanks to Pavel Fric 2021-02-20 * Implemented palette serialization methods. Fixed lost attributes when piano scene is rebuilt. 2021-02-19 * Implemented ticket #26: customizable texture for black and white keys 2021-02-17 * Implemented ticket #25: chromatic scale highlight palette 2021-02-10 * fix for ticket #27: error parsing a wrk file 2021-02-03 * copyright years updated * drumstick-guiplayer: fixed stop playback, removed Overture mimetype from desktop file * library headers: fix for ticket #23 2020-12-29 * release 2.0.0 2020-11-02 * documentation updated 2020-10-12 * designer plugin renamed to "drumstick-vpiano-plugin" and fixed deploy location. Central C naming revised 2020-10-08 * License upgrade: GPLv3 or later. Translations updated. 2020-09-28 * fixed SequencerOutputThread troubles finishing songs properly * enhanced guiplayer usability 2020-09-25 * plugins versioning, allowing runtime coexistence between plugins of drumstick-1 and drumstick-2 2020-09-18 * Enabled translations for drumstick-widgets and GUI utils 2020-09-16 * fixed ticket #2 Removed unmaintained OVE support 2020-09-11 * Piano palette refactoring * New unit tests for the Widgets library 2020-09-09 * fixed ticket #20 Same names for ALSA Sequencer clients of two hw USB controllers 2020-09-04 * fixed ticket #22 implementation: better rendering of note names in piano keyboard widget 2020-03-20 * New library drumstick-gui, applied to drumstick-vpiano 2020-01-02 * Synchronization of EAS Synth code with upstream AOSP repository ('android10' branch) 2019-12-30 * Code and namespaces reorganization. drumstick-alsa classes placed in the new 'drumstick::ALSA' namespace 2019-12-19 * Revised ALSA RT plugins. ALSA Client is now created only when needed 2019-12-16 * Modernization of the cmake buildsystem, producing cmake configuration scripts 2019-09-01 * release 1.1.3 2019-08-29 * release preparations 2019-07-07 * Avoid endless loops on unexpected end of input. 2019-07-06 * Fix for ticket #17: CoreMIDI.framework using wrong name-case 2019-07-06 * Generate SMFError when the parser finds unexpected end of input. Fix for ticket #16 2019-07-05 * Fix for ticket #16: bad MIDI files (drumstick-file) 2019-07-02 * Fixed macOS deprecation warnings 2019-01-29 * general cleanups and bringing back the fluidsynth backend 2019-01-28 * Fix for ticket #14: migration to full GNUInstallDirs support 2019-01-27 * modernization started of the CMake build system 2019-01-26 * Library includes reorganization 2019-01-20 * Fixed drumstick-file unit test * Tweaks on qmake build system 2019-01-15 * Custom commandline parser replaced by standard Qt5 QCommandLineParser 2019-01-13 * Removed custom commanline parser class, replaced by standard Qt5 QCommandLineParser 2019-01-08 * fixed spurious error message for ALSA input 2019-01-07 * Fixed differences between unix and windows semantics 2019-01-06 * Network backend: support for IPv6 2018-11-25 * release 1.1.2 2018-11-24 * Fix for ticket #13 - some macOS input events lost * Added some bundle metadata for macOS * Solution for ticket #8: Path for plugins hardcoded 2018-02-24 * release 1.1.1 2018-01-07 * disabled fluidsynth output driver * Fixed build on macOS < 10.11, thanks to Andreas 2017-08-14 * fixed unit test for more tolerance * release preparations 2017-08-13 * fix for ticket #11: replaced assert() by Q_ASSERT() * fix for ticket #9: removed ALSA/Linux dependency where possible. 2017-05-10 * rt: fixed pitch bend events on several output backends 2017-04-16 * fixed tab order on forms 2017-04-03 * added keywords to the desktop files. Patch by Ross Gammon 2016-09-25 * release 1.1.0 2016-09-11 * reverted audio backend for sonivox eas to pulseaudio again * rt: settings for sonivox eas and mac native synth * vpiano: dialogs for synth settings 2016-08-18 * release 1.1.0 preparation * removed four utilities, now implemented as unit tests 2016-08-17 * drumstick-rt: fix backendmanager initialization, added two new methods to retrieve backends by name * documentation updated * copyright years updated 2016-07-03 * guiplayer accepts a single file argument in the command line: mid, kar, wrk, and ove * Missing cmake scripts license, patch by Maximiliano Curia 2016-05-22 * Fix for mac osx static builds 2016-05-21 * Fix for static build using the new backends 2016-05-16 * Fix for cmake/qmake builds on mac 2016-05-15 * new backend: Apple DLS Synth 2016-05-08 * version number changed provisionally to 1.0.99 * new backend: sonivox eas synthesizer for Linux 2016-02-17 * Fix build with GCC 6, patch by Robin Lee 2016-01-30 * guiplayer: there is no need for a quit() slot. Fix for hanging notes when closing the window with the corner icon. * Use GNUInstallDirs to install arch-independent data, patch by Heiko Becker 2015-12-29 * release 1.0.2 2015-10-10 * RT library: fixed ticket #6 - MIDI input connection on Mac OSX 2015-08-20 * release 1.0.1 2015-04-26 * RT library: fixed ticket #4: ALSA Midi Input not working 2014-11-22 * RT library: fixed windows midi input 2014-08-30 * release 1.0.0 2014-08-02 * vpiano using RT library 2014-07-26 * documentation updates 2014-04-27 * RT library: OSS backend 2014-04-13 * RT library: FluidSynth backend 2014-03-30 * RT library: Windows backend 2014-02-09 * RT library: Mac OSX backend 2014-02-09 * RT library: Network and ALSA backends 2013-12-31 * Qt5 compatibility 2010-09-13 * fixed dumpove: file header text 2010-09-08 * documentation updated * release 0.5.0 2010-09-08 * remaining warnings removed * guiplayer simplified, and optimizations * compile with -fvisibility=hidden if it is available * compile always with -fexceptions * fixes in both buildsystems, cmake and qmake 2010-09-02 * Use RealtimeKit support for the MIDI input thread. 2010-08-31 * OVE file format support, by Rui Fan * guiplayer adds OVE format playback 2010-07-24 * Fixed static build support 2010-07-12 * removed PCH build option * release 0.4.1 2010-07-11 * Fixed bug in class SequencerInputThread: realtime priority must be applied in run() instead of start() to avoid changing the scheduling policy of the parent. This is a problem when running FluidSynth in systems affected by a glib-2.22 bug that has not yet been fixed. Reference: https://bugzilla.gnome.org/show_bug.cgi?id=599079 2010-07-07 * smfplayer renamed as guiplayer, with a new windows layout and supporting Cakewalk WRK files playback * release 0.4.0 2010-07-03 * subdirectory "tests" renamed as "utils". * visibility attribute for public classes. 2010-06-10 * Compile fix for 0.3.2 * release 0.3.2a 2010-06-09 * Command line arguments for all the utilities/test programs. 2010-05-28 * fix a crash in drumstick-sysinfo when a timer module is not loaded. 2010-05-13 * Man pages for the utilities/test programs. 2010-05-10 * New test program: DrumGrid 2010-04-19 * Release 0.3.1 * Allow to build drumstick-file library under Windows * New method MidiClient::parseAddress() replacing the ALSA function snd_seq_parse_address() in MidiPort::subscribeTo() and similar methods. * Fixed MidiClient::getAvailableInputs() and getAvailableOutputs() forcing to always retrieve the clients list. 2010-03-09 * Release 0.3.0 * API changes: SequencerEvent::isChannel() returns true for SND_SEQ_EVENT_NOTE QueueTimer::setId(const TimerId& id) new method overload Timer::bestGlobalTimerId() new static function getRuntimeALSALibraryVersion() new global function getRuntimeALSALibraryNumber() new global function getRuntimeALSADriverVersion() new global function getRuntimeALSADriverNumber() new global function 2010-03-02 * New class QWrk, for reading Cakewalk files 2010-02-17 * Split: drumstick-file and drumstick-alsa 2010-01-07 * Renamed to 'drumstick' and moved to a new project repository 2009-12-27 * Release 0.2.0 * API changes: SequencerInputThread::start() added a priority parameter SequencerEvent::isChannel() new static method SequencerOutputThread::stopped() method removed, converted into a signal SequencerOutputThread::start() added a priority parameter SequencerOutputThread::shutupSound() method removed SequencerOutputThread::stopRequested() method added QSmfPrivate class added QSmf::writeMetaEvent() new method overload QSmf::getTextCodec() new method QSmf::setTextCodec() new method QSmf::signalSMFVariable() signal dropped QSmf::signalSMFMetaUnregistered() signal added Subscriber::operator==() removed unimplemented operator prototype 2009-08-27 * Public release 0.1.0 2008-12-29 0.0.2pre5 * Snapshot included in kmetronome-0.9.0 and kmidimon-0.6.0 2008-11-09 0.0.2pre1 * Initial pre-release 2008-05-12 0.0.1 * Development started drumstick-2.9.0/NEWS0000644000175000017500000000035314541630232013233 0ustar pedropedroBarcelona. Thursday, January 7, 2010 Project "aseqmm" has been renamed to "drumstick" RSS newsfeed available at: https://sourceforge.net/p/drumstick/news/feed.rss Activity RSS feed: https://sourceforge.net/p/drumstick/activity/feed drumstick-2.9.0/TODO0000644000175000017500000000066214541630232013227 0ustar pedropedroFunctions from ALSA library reported by chkcoverage. The following functions won't be used in drumstick. MISC (event filter) snd_seq_change_bit snd_seq_get_bit snd_seq_set_bit snd_seq_unset_bit CLIENT - private functions snd_seq_hw_open snd_seq_hw_ops snd_seq_create_event snd_seq_event_types TIMER - private functions snd_timer_async snd_timer_name snd_timer_hw_open snd_timer_nonblock snd_timer_query_hw_open snd_timer_type drumstick-2.9.0/install.md0000644000175000017500000001162514541630232014530 0ustar pedropedro## Basic build commands Using a Linux terminal... ~~~bash $ cd drumstick-x.y.z $ mkdir -p build $ cmake -S . -B build (or ccmake -S . -B build) (or cmake-gui -S . -B build) (or cmake -S . -B build -options, see below...) ~~~ For simple test/development scenarios you can also use Qmake, but the Qmake-based build system provides only minimal functionality compared to CMake. By default, CMake generates Makefiles. You may also use Ninja instead, with the cmake option `-G Ninja` at configuration time. ~~~bash $ cmake --build build $ sudo cmake --install build $ sudo ldconfig (not needed if you only want STATIC_DRUMSTICK) ~~~ ## Requirements Minimum supported versions: * CMake 3.16 * C++17 compiler * Qt6 >= 6.2 (or Qt5 >= 5.9) When using Qt6, Qt6Core5Compat is required for Drumstick::File * For Linux: ALSA 1.x * RT backends for * Linux: ALSA * macOS: CoreMIDI * Windows: WinMM * Unix: OSS * Synthesizers (output backends): Sonivox, FluidSynth, Apple DLS Synth * All: Network - ipMIDI (IPv4, IPv6): QtNetwork * shared-mime-info 0.30 See http://freedesktop.org/wiki/Software/shared-mime-info The utility "update-mime-database" must be executed after installing the library "drumstick-file" and the "drumstick.xml" file. This is automatically done by the cmake build system unless you defined DESTDIR. In this case, your package manager should perform it as a post-install step. * Doxygen 1.5 See http://www.doxygen.org If you want to generate the HTML documentation for the libraries If you want to generate and install the man pages, the build system can do it if you have installed in your system the following packages: * xsltproc program. * docbook XSLT stylesheets. The package names depend on the Linux distribution. For Debian they are: xsltproc, docbook-xsl and docbook-xml. For openSUSE: libxslt, docbook_4, and docbook-xsl-stylesheets. RealtimeKit actions are called through DBus, so it is not a direct dependency. ## Optional CMake parameters -DSTATIC_DRUMSTICK=YES|ON|1 Build static libraries instead of a shared object. -DSTATIC_DRUMSTICK=NO|OFF|0 Build dynamic libraries (default). -DCMAKE_BUILD_TYPE=Debug Compile with debug flags enabled. -DCMAKE_BUILD_TYPE=Release Compile without debug flags, and optimization enabled. -DCMAKE_CXX_FLAGS="-W -Wall" Specify custom compilation flags. -DCMAKE_INSTALL_PREFIX=/usr/local Specify the desired install prefix. -DUSE_DBUS=YES|ON|1 Build DBus support, required to use RealtimeKit. -DUSE_DBUS=NO|OFF|0 Don't include DBus support (default). -DBUILD_DOCS=YES|ON|1 Build Doxygen documentation and man pages (default in Unix). -DBUILD_DOCS=NO|OFF|0 Don't build Doxygen documentation nor man pages. Note: the last option only creates a "doxygen" target. You still need to build the target yourself if you want to generate the actual documentation. -DBUILD_UTILS=YES|ON|1 Build utilities and example programs (default). -DBUILD_UTILS=NO|OFF|0 Don't build utilities and example programs. -DBUILD_TESTING=YES|ON|1 Build unit tests (default). -DBUILD_TESTING=NO|OFF|0 Don't build unit tests -DBUILD_FRAMEWORKS=YES|ON|1 Build macOS style frameworks (default on macOS). -DBUILD_FRAMEWORKS=NO|OFF|0 Don't build macOS style frameworks, build Unix style dynamic libraries (.dylib). -DBUILD_ALSA=YES|ON|1 Build Drumstick::ALSA (default on Linux). -DBUILD_ALSA=NO|OFF|0 Don't build Drumstick::ALSA (default on non Linux Operating Systems). -DBUILD_FILE=YES|ON|1 Build Drumstick::File (default). -DBUILD_FILE=NO|OFF|0 Don't build Drumstick::File. -DBUILD_RT=YES|ON|1 Build Drumstick::RT (default). -DBUILD_RT=NO|OFF|0 Don't build Drumstick::RT. -DBUILD_WIDGETS=YES|ON|1 Build Drumstick::Widgets (default). -DBUILD_WIDGETS=NO|OFF|0 Don't build Drumstick::Widgets. -DUSE_NETWORK=YES|ON|1 Build the ipMIDI Network RT backend (default) Triggers a fatal error at configuration time if libQtNetwork is not available. -DUSE_NETWORK=NO|OFF|0 Don't build the ipMIDI Network RT backend. -DUSE_PULSEAUDIO=YES|ON|1 Build the SonivoxEAS RT output backend (default). Triggers a fatal error at configuration time if libpulse-simple is not available. -DUSE_PULSEAUDIO=NO|OFF|0 Don't build the SonivoxEAS RT output backend. Note: FluidSynth has also a PulseAudio driver, which is independent of the last option. -DUSE_FLUIDSYNTH=YES|ON|1 Build the FluidSynth RT output backend (default). Triggers a fatal error at configuration time if libfluidsynth is not available. -DUSE_FLUIDSYNTH=NO|OFF|0 Don't build the FluidSynth RT output backend. -DUSE_QT5=NO|OFF|0 Build with Qt6 (default). Note: When using Qt6, the Core5Compat additional library is required for Drumstick::File. -DUSE_QT5=YES|ON|1 Build with Qt5 instead of Qt6. -DUSE_SONIVOX=YES|ON|1 Build Sonivox RT backend (default) Triggers a fatal error at configuration time if libsonivox is not available. -DUSE_SONIVOX=NO|OFF|0 Don't build SonivoxEAS RT output backend. drumstick-2.9.0/readme.md0000644000175000017500000001370314541630232014316 0ustar pedropedro# Drumstick Libraries [![Linux Build and Test](https://github.com/pedrolcl/drumstick/actions/workflows/cmake.yml/badge.svg?branch=devel)](https://github.com/pedrolcl/drumstick/actions/workflows/cmake.yml) Drumstick is a set of MIDI libraries using C++/Qt idioms and style. Includes a C++ wrapper around the ALSA library sequencer interface: ALSA sequencer provides software support for MIDI technology on Linux. A complementary library provides classes for processing SMF (Standard MIDI files: .MID/.KAR), RIFF RMID (*.rmi) and Cakewalk (.WRK) file formats. A multiplatform realtime MIDI I/O library and a GUI Widgets libraries are also provided for Linux, Windows, and Mac OSX. Currently, there are four libraries designed to work together if/when needed: * **Drumstick::ALSA** is a Linux only C++/Qt wrapper around the ALSA Sequencer API. ALSA sequencer provides software support for MIDI technology on Linux. * **Drumstick::File** provides easy multiplatform file I/O for Standard MIDI Files (.mid), RIFF RMID (.rmi) and Cakewalk (.wrk) file formats. * **Drumstick::RT** is a realtime MIDI I/O library with pluggable backends. It uses Drumstick::ALSA on Linux, and other native frameworks on macOS and Windows. * **Drumstick::Widgets** contains MIDI widgets, including a Virtual Piano used by VMPK among other programs. **Drumstick::ALSA** was the first library developed under the Drumstick umbrella, and is available only on Linux, because ALSA Sequencer is an exclusive Linux technology. For realtime I/O applications you can use the **Drumstick::RT** library which is multiplatform, and only depends on **Drumstick::ALSA** in Linux for its ALSA Sequencer backend. Other multiplatform backends are: Network/[ipMIDI](https://www.nerds.de/en/ipmidi.html) and [Fluidsynth](https://github.com/FluidSynth/fluidsynth). The [Sonivox](https://github.com/pedrolcl/sonivox) backend needs PulseAudio, which is available on most Unix systems. There are ten examples in the source tree, under the utils/ directory: * drumgrid: GUI program. Simple drum patterns. Depends on Drumstick::ALSA. * dumpmid: CLI program. Prints received MIDI events. Depends on Drumstick::ALSA. See also [kmidimon](https://kmidimon.sourceforge.io) * dumprmi: CLI program. Prints and converts RIFF MIDI files. Depends on Drumstick::File. * dumpsmf: CLI program. Prints standard MIDI files. Depends on Drumstick::File. * dumpwrk: CLI program. Prints Cakewalk/Sonar MIDI files. Depends on Drumstick::File. See also [wrk2mid](https://wrk2mid.sourceforge.io) * guiplayer: GUI program. Plays SMF and Cakewalk files. Depends on Drumstick::ALSA and Drumstick::File. See also [dmidiplayer](https://dmidiplayer.sourceforge.io) * metronome: CLI program. Simple command line MIDI metronome. Depends on Drumstick::ALSA. See also [kmetronome](https://kmetronome.sourceforge.io) * playsmf: CLI program. SMF player. Depends on Drumstick::ALSA and Drumstick::File. * sysinfo: CLI program. Prints information about the ALSA sequencer subsystem. Depends on Drumstick::ALSA. * vpiano: GUI program. A simple Virtual Piano Keyboard GUI application. Depends on Drumstick::RT. See also [VMPK](http://vmpk.sourceforge.io). And you can also see independent applications using this library: * [dmidiplayer](https://sourceforge.net/p/dmidiplayer): Multiplatform MIDI file player with many features. * [VMPK](https://sourceforge.net/p/vmpk): Multiplatform Virtual MIDI Piano Keyboard. * [kmetronome](https://sourceforge.net/p/kmetronome): MIDI metronome for Linux. * [kmidimon](https://sourceforge.net/p/kmidimon): MIDI monitor for Linux. * [wrk2mid](https://sourceforge.net/p/wrk2mid): Command line utility to convert WRK files to SMF. Here is a diagram about the relationship between the libraries and applications: ![Drumstick ecosystem](doc/drumstick-ecosystem.png) Here is another view in table format of the relationships: | | Drumstick::ALSA | Drumstick::File | Drumstick::RT | Drumstick::Widgets | |-------------|:------------------:|:------------------:|:------------------:|:------------------:| |Utilities: | | | | | | drumgrid | :white_check_mark: | :x: | :x: | :x: | | dumpmid | :white_check_mark: | :x: | :x: | :x: | | dumprmi | :x: | :white_check_mark: | :x: | :x: | | dumpsmf | :x: | :white_check_mark: | :x: | :x: | | dumpwrk | :x: | :white_check_mark: | :x: | :x: | | guiplayer | :white_check_mark: | :white_check_mark: | :x: | :x: | | metronome | :white_check_mark: | :x: | :x: | :x: | | playsmf | :white_check_mark: | :white_check_mark: | :x: | :x: | | sysinfo | :white_check_mark: | :x: | :x: | :x: | | vpiano | :x: | :x: | :white_check_mark: | :white_check_mark: | |Applications:| | | | | | dmidiplayer | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | VMPK | :x: | :x: | :white_check_mark: | :white_check_mark: | | kmetronome | :white_check_mark: | :x: | :x: | :x: | | kmidimon | :white_check_mark: | :white_check_mark: | :x: | :x: | | wrk2mid | :x: | :white_check_mark: | :x: | :x: | The main web site of this project is [drumstick.sourceforge.io](https://drumstick.sourceforge.io) See also: * [Downloads](https://sourceforge.net/projects/drumstick/files/) * [Online documentation](https://drumstick.sourceforge.io/docs/index.html) * [Build and install documentation](install.md) drumstick-2.9.0/drumstick-alsa.pc.in0000644000175000017500000000051414541630232016407 0ustar pedropedroprefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} libdir=@pkgconfig_libdir@ includedir=@pkgconfig_includedir@ Name: drumstick-alsa Version: @PROJECT_VERSION@ Description: MIDI Sequencer C++ Library Bindings for ALSA and Qt URL: http://sourceforge.net/projects/drumstick Libs: -L${libdir} -ldrumstick-alsa Cflags: -I${includedir} drumstick-2.9.0/drumstick-file.pc.in0000644000175000017500000000046514541630232016413 0ustar pedropedroprefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} libdir=@pkgconfig_libdir@ includedir=@pkgconfig_includedir@ Name: drumstick-file Version: @PROJECT_VERSION@ Description: MIDI File C++ Library for Qt URL: http://sourceforge.net/projects/drumstick Libs: -L${libdir} -ldrumstick-file Cflags: -I${includedir} drumstick-2.9.0/drumstick-rt.pc.in0000644000175000017500000000046514541630232016121 0ustar pedropedroprefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} libdir=@pkgconfig_libdir@ includedir=@pkgconfig_includedir@ Name: drumstick-rt Version: @PROJECT_VERSION@ Description: MIDI Realtime C++ Library for Qt URL: http://sourceforge.net/projects/drumstick Libs: -L${libdir} -ldrumstick-rt Cflags: -I${includedir} drumstick-2.9.0/drumstick-widgets.pc.in0000644000175000017500000000047614541630232017144 0ustar pedropedroprefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} libdir=@pkgconfig_libdir@ includedir=@pkgconfig_includedir@ Name: drumstick-widgets Version: @PROJECT_VERSION@ Description: MIDI Widgets C++ Library for Qt URL: http://sourceforge.net/projects/drumstick Libs: -L${libdir} -ldrumstick-widgets Cflags: -I${includedir} drumstick-2.9.0/drumstick.spec.in0000644000175000017500000001326214541630232016025 0ustar pedropedro# spec file for package drumstick (Version @PROJECT_VERSION@) # # MIDI Sequencer C++ Library Bindings for Qt # Copyright (C) 2005-2023 Pedro Lopez-Cabanillas # # This file and all modifications and additions to the pristine # package are under the same license as the package itself. # # norootforbuild Name: drumstick Version: @PROJECT_VERSION@ Release: 1 License: GPL v3 or later Summary: MIDI Sequencer C++ Library Bindings Group: Productivity/Multimedia/Sound/Midi URL: http://drumstick.sourceforge.net Source: %{name}-%{version}.tar.bz2 BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildRequires: cmake BuildRequires: alsa-lib-devel BuildRequires: qt5-qtbase-devel BuildRequires: fluidsynth-devel BuildRequires: pulseaudio-libs-devel BuildRequires: doxygen BuildRequires: graphviz BuildRequires: libxslt BuildRequires: docbook-utils BuildRequires: docbook-style-xsl Requires: shared-mime-info %description This package includes test and example programs for drumstick libraries. Authors: -------- Pedro Lopez-Cabanillas %package -n libdrumstick-file2 Summary: MIDI Sequencer C++ Library Group: System/Libraries %description -n libdrumstick-file2 MIDI Sequencer C++ Library Bindings for Qt This library includes classes providing file input and output in formats commonly used by MIDI programs. Currently, SMF (standard MIDI file) read/write and WRK (Cakewalk) file read are supported. Authors: -------- Pedro Lopez-Cabanillas %package -n libdrumstick-alsa2 Summary: MIDI Sequencer C++ Library Group: System/Libraries %description -n libdrumstick-alsa2 MIDI Sequencer C++ Library Bindings for Qt and ALSA. This library includes the ALSA Sequencer library classes, providing MIDI recording and playback functionality to C++/Qt programs. Authors: -------- Pedro Lopez-Cabanillas %package -n libdrumstick-rt2 Summary: MIDI Realtime IO C++ Library Group: System/Libraries %description -n libdrumstick-rt2 MIDI Realtime IO C++ Library for Qt This library includes the RT library classes, providing MIDI realtime IO functionality to C++/Qt programs. Authors: -------- Pedro Lopez-Cabanillas %package -n libdrumstick-widgets2 Summary: MIDI Widgets C++ Library Group: System/Libraries %description -n libdrumstick-widgets2 MIDI Widgets C++ Library for Qt This library includes the Widgets library, providing GUI, MIDI related, components for C++/Qt programs. Authors: -------- Pedro Lopez-Cabanillas %package -n libdrumstick-devel Summary: Development package for the drumstick libraries Group: Development/Libraries/C and C++ Requires: libdrumstick-file2 = %{version} Requires: libdrumstick-alsa2 = %{version} Requires: libdrumstick-rt2 = %{version} Requires: libdrumstick-widgets2 = %{version} Requires: glibc-devel libstdc++-devel alsa-lib-devel qt5-qtbase-devel %description -n libdrumstick-devel This package contains the files needed to compile programs that use the libdrumstick libraries. Authors: -------- Pedro Lopez-Cabanillas %package -n libdrumstick-doc Summary: Development documentation package for the drumstick libraries Group: Documentation/Other %description -n libdrumstick-doc This package contains the developer's documentation of the drumstick libraries. Authors: -------- Pedro Lopez-Cabanillas %prep %setup -q %build CXXFLAGS="$RPM_OPT_FLAGS -g -fexceptions" \ cmake . -DSTATIC_DRUMSTICK=0 \ -DCMAKE_INSTALL_PREFIX=%{_prefix} \ -DLIB_SUFFIX=$(echo %_lib | cut -b4-) make %{?jobs:-j %jobs} VERBOSE=1 make doxygen %install make install DESTDIR=$RPM_BUILD_ROOT %post %{_bindir}/update-mime-database %{_datadir}/mime %postun %{_bindir}/update-mime-database %{_datadir}/mime %post -n libdrumstick-file2 -p /sbin/ldconfig %postun -n libdrumstick-file2 -p /sbin/ldconfig %post -n libdrumstick-alsa2 -p /sbin/ldconfig %postun -n libdrumstick-alsa2 -p /sbin/ldconfig %post -n libdrumstick-rt2 -p /sbin/ldconfig %postun -n libdrumstick-rt2 -p /sbin/ldconfig %post -n libdrumstick-widgets2 -p /sbin/ldconfig %postun -n libdrumstick-widgets2 -p /sbin/ldconfig %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-, root, root) %doc AUTHORS COPYING INSTALL NEWS README TODO ChangeLog %doc %{_mandir}/* %{_datadir}/icons/hicolor/*/*/* %{_datadir}/applications/* %{_bindir}/* %files -n libdrumstick-file2 %defattr(-,root,root) %{_libdir}/libdrumstick-file.so.* %{_datadir}/mime/packages/* %files -n libdrumstick-alsa2 %defattr(-,root,root) %{_libdir}/libdrumstick-alsa.so.* %files -n libdrumstick-rt2 %defattr(-,root,root) %{_libdir}/libdrumstick-rt.so.* %{_libdir}/drumstick/*.so %files -n libdrumstick-widgets2 %defattr(-,root,root) %dir %{_datadir}/drumstick %{_libdir}/libdrumstick-widgets.so.* %{_datadir}/drumstick/* %files -n libdrumstick-devel %defattr(-, root, root) %dir %{_includedir}/drumstick %{_libdir}/libdrumstick-file.so %{_libdir}/libdrumstick-alsa.so %{_libdir}/libdrumstick-rt.so %{_libdir}/libdrumstick-widgets.so %{_includedir}/drumstick.h %{_includedir}/drumstick/*.h %{_libdir}/pkgconfig/*.pc %files -n libdrumstick-doc %defattr(-, root, root) %doc doc/html/* %changelog * Pedro Lopez-Cabanillas 2.0.0 - New version * Sat Aug 30 2014 Pedro Lopez-Cabanillas 1.0.0 - New version * Thu Sep 9 2010 Pedro Lopez-Cabanillas 0.5.0 - New version drumstick-2.9.0/drumstick.pro0000644000175000017500000000074714541630232015272 0ustar pedropedroTEMPLATE = subdirs SUBDIRS += \ library \ utils \ tests utils.depends = library tests.depends = library requires(equals(QT_MAJOR_VERSION, 5)|equals(QT_MAJOR_VERSION, 6)) equals(QT_MAJOR_VERSION, 5):lessThan(QT_MINOR_VERSION, 9) { message("Cannot build VMPK with Qt $${QT_VERSION}") error("Use Qt 5.9 or newer") } equals(QT_MAJOR_VERSION, 6):lessThan(QT_MINOR_VERSION, 2) { message("Cannot build VMPK with Qt $${QT_VERSION}") error("Use Qt 6.2 or newer") } drumstick-2.9.0/drumstick.xml0000644000175000017500000000205414541630232015263 0ustar pedropedro Cakewalk project file Cakewalk project file Archivo de proyecto Cakewalk RMID file Archivo RMID drumstick-2.9.0/configure0000755000175000017500000000035314541630232014443 0ustar pedropedro#!/bin/bash # a typical configuration for production usage... mkdir -p build cd build cmake .. -DCMAKE_CXX_FLAGS="-W -Wall" \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DSTATIC_DRUMSTICK=NO \ -DUSE_DBUS=YES drumstick-2.9.0/configure_dbg0000755000175000017500000000035314541630232015257 0ustar pedropedro#!/bin/bash # a typical configuration for development usage... mkdir -p build cd build cmake .. -DCMAKE_CXX_FLAGS="-W -Wall" \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DSTATIC_DRUMSTICK=YES \ -DUSE_DBUS=YES drumstick-2.9.0/Doxyfile.in0000644000175000017500000035572014541630232014662 0ustar pedropedro# Doxyfile 1.9.5 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). # # Note: # # Use doxygen to compare the used configuration file with the template # configuration file: # doxygen -x [configFile] # Use doxygen to compare the used configuration file with the template # configuration file without replacing the environment variables or CMake type # replacement variables: # doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = drumstick # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @PROJECT_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = C++ MIDI libraries using Qt objects, idioms, and style. # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = @CMAKE_CURRENT_SOURCE_DIR@/icons/drumstick_48.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = doc # If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 # sub-directories (in 2 levels) under the output directory of each output format # and will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to # control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO # Controls the number of sub-directories that will be created when # CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every # level increment doubles the number of directories, resulting in 4096 # directories at level 8 which is the default and also the maximum value. The # sub-directories are organized in 2 levels, the first level always has a fixed # numer of 16 directories. # Minimum value: 0, maximum value: 8, default value: 8. # This tag requires that the tag CREATE_SUBDIRS is set to YES. CREATE_SUBDIRS_LEVEL = 8 # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, # Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English # (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, # Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with # English messages), Korean, Korean-en (Korean with English messages), Latvian, # Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, # Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, # Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = YES # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = NO # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES # If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = YES # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # By default Python docstrings are displayed as preformatted text and doxygen's # special commands cannot be used. By setting PYTHON_DOCSTRING to NO the # doxygen's special commands can be used and the contents of the docstring # documentation blocks is shown as doxygen documentation. # The default value is: YES. PYTHON_DOCSTRING = YES # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". Note that you cannot put \n's in the value part of an alias # to insert newlines (in the resulting output). You can put ^^ in the value part # of an alias to insert a newline as if a physical newline was in the original # file. When you need a literal { or } or , in the value part of an alias you # have to escape them by means of a backslash (\), this can lead to conflicts # with the commands \{ and \} for these it is advised to use the version @{ and # @} or use a double escape (\\{ and \\}) ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, # Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, # VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files # as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. When specifying no_extension you should add # * to the FILE_PATTERNS. # # Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 5 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 # The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, # which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. NUM_PROC_THREADS = 1 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = YES # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If this flag is set to YES, the name of an unnamed parameter in a declaration # will be determined by the corresponding definition. By default unnamed # parameters remain unnamed in the output. # The default value is: YES. RESOLVE_UNNAMED_PARAMS = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # declarations. If set to NO, these declarations will be included in the # documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # With the correct setting of option CASE_SENSE_NAMES doxygen will better be # able to match the capabilities of the underlying filesystem. In case the # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that # are not case sensitive the option should be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On # Windows (including Cygwin) and MacOS, users should typically set this option # to NO, whereas on Linux or other Unix flavors it should typically be set to # YES. # Possible values are: SYSTEM, NO and YES. # The default value is: SYSTEM. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = YES # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_HEADERFILE tag is set to YES then the documentation for a class # will show which file needs to be included to use the class. # The default value is: YES. SHOW_HEADERFILE = YES # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. See also section "Changing the # layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as documenting some parameters in # a documented function twice, or documenting parameters that don't exist or # using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete # function parameter documentation. If set to NO, doxygen will accept that some # parameters have no documentation without warning. # The default value is: YES. WARN_IF_INCOMPLETE_DOC = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong parameter # documentation, but not about the absence of documentation. If EXTRACT_ALL is # set to YES then this flag will automatically be disabled. See also # WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = YES # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS # then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but # at the end of the doxygen process doxygen will return with a non-zero status. # Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # In the $text part of the WARN_FORMAT command it is possible that a reference # to a more specific place is given. To make it easier to jump to this place # (outside of doxygen) the user can define a custom "cut" / "paste" string. # Example: # WARN_LINE_FORMAT = "'vi $file +$line'" # See also: WARN_FORMAT # The default value is: at line $line of file $file. WARN_LINE_FORMAT = "at line $line of file $file" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). In case the file specified cannot be opened for writing the # warning and error messages are written to standard error. When as file - is # specified the warning and error messages are written to standard output # (stdout). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @CMAKE_BINARY_DIR@/doc/drumstick-devel.doc.txt \ @CMAKE_SOURCE_DIR@/library # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: # https://www.gnu.org/software/libiconv/) for the list of possible encodings. # See also: INPUT_FILE_ENCODING # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # This tag can be used to specify the character encoding of the source files # that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify # character encoding on a per file pattern basis. Doxygen will compare the file # name with each pattern and apply the encoding instead of the default # INPUT_ENCODING) if there is a match. The character encodings are a list of the # form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding # "INPUT_ENCODING" for further information on supported encodings. INPUT_FILE_ENCODING = # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # Note the list of default checked file patterns might differ from the list of # default file extension mappings. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, # *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C # comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, # *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.cpp \ *.h # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = @CMAKE_SOURCE_DIR@/library/include/drumstick.h \ @CMAKE_SOURCE_DIR@/library/rt-backends # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # ANamespace::AClass, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = @CMAKE_SOURCE_DIR@/utils/dumpmid \ @CMAKE_SOURCE_DIR@/utils/dumpwrk \ @CMAKE_SOURCE_DIR@/utils/playsmf \ @CMAKE_SOURCE_DIR@/utils/dumpsmf \ @CMAKE_SOURCE_DIR@/utils/dumprmi \ @CMAKE_SOURCE_DIR@/utils/metronome \ @CMAKE_SOURCE_DIR@/utils/sysinfo \ @CMAKE_SOURCE_DIR@/utils/vpiano \ @CMAKE_SOURCE_DIR@/utils/drumgrid \ @CMAKE_SOURCE_DIR@/utils/guiplayer # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = YES # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that doxygen will use the data processed and written to standard output # for further processing, therefore nothing else, like debug statements or used # commands (so in case of a Windows batch file always use @echo OFF), should be # written to standard output. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = # The Fortran standard specifies that for fixed formatted Fortran code all # characters from position 72 are to be considered as comment. A common # extension is to allow longer lines before the automatic comment starts. The # setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can # be processed before the automatic comment starts. # Minimum value: 7, maximum value: 10000, default value: 72. FORTRAN_COMMENT_AFTER = 72 #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the # clang parser (see: # http://clang.llvm.org/) for more accurate parsing at the cost of reduced # performance. This can be particularly helpful with template rich C++ code for # which doxygen's built-in parser lacks the necessary type information. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO # If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS # tag is set to YES then doxygen will add the directory of each input to the # include path. # The default value is: YES. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_ADD_INC_PATHS = YES # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_OPTIONS = # If clang assisted parsing is enabled you can provide the clang parser with the # path to the directory containing a file called compile_commands.json. This # file is the compilation database (see: # http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the # options used when the source files were built. This is equivalent to # specifying the -p option to a clang tool, such as clang-check. These options # will then be passed to the parser. Any options specified with CLANG_OPTIONS # will be added as well. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. CLANG_DATABASE_PATH = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = NO # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE tag can be used to specify if the generated HTML output # should be rendered with a dark or light theme. Default setting AUTO_LIGHT # enables light output unless the user preference is dark output. Other options # are DARK to always use dark mode, LIGHT to always use light mode, AUTO_DARK to # default to dark mode unless the user prefers light mode, and TOGGLE to let the # user toggle between dark and light mode via a button. # Possible values are: LIGHT Always generate light output., DARK Always generate # dark output., AUTO_LIGHT Automatically set the mode according to the user # preference, use light mode if no preference is set (the default)., AUTO_DARK # Automatically set the mode according to the user preference, use dark mode if # no preference is set. and TOGGLE Allow to user to switch between light and # dark mode via a button.. # The default value is: AUTO_LIGHT. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE = AUTO_LIGHT # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via JavaScript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have JavaScript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: # https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To # create a documentation set, doxygen will generate a Makefile in the HTML # output directory. Running make will produce the docset in that directory and # running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag determines the URL of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDURL = # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # on Windows. In the beginning of 2021 Microsoft took the original page, with # a.o. the download links, offline the HTML help workshop was already many years # in maintenance mode). You can download the HTML help workshop from the web # archives at Installation executable (see: # http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo # ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location (absolute path # including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to # run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine tune the look of the index (see "Fine-tuning the output"). As an # example, the default style sheet generated by doxygen has an example that # shows how to put an image at the root of the tree instead of the PROJECT_NAME. # Since the tree basically has the same information as the tab index, you could # consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES # When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the # FULL_SIDEBAR option determines if the side bar is limited to only the treeview # area (value NO) or if it should extend to the full height of the window (value # YES). Setting this to YES gives a layout similar to # https://docs.readthedocs.io with more room for contents, but less room for the # project logo, title, and description. If either GENERATE_TREEVIEW or # DISABLE_INDEX is set to NO, this option has no effect. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. FULL_SIDEBAR = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email # addresses. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. OBFUSCATE_EMAILS = YES # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for # the HTML output. These images will generally look nicer at scaled resolutions. # Possible values are: png (the default) and svg (looks nicer but requires the # pdf2svg or inkscape tool). # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FORMULA_FORMAT = png # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands # to create new LaTeX commands to be used in formulas as building blocks. See # the section "Including formulas" for details. FORMULA_MACROFILE = # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # With MATHJAX_VERSION it is possible to specify the MathJax version to be used. # Note that the different versions of MathJax have different requirements with # regards to the different settings, so it is possible that also other MathJax # settings have to be changed when switching between the different MathJax # versions. # Possible values are: MathJax_2 and MathJax_3. # The default value is: MathJax_2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_VERSION = MathJax_2 # When MathJax is enabled you can set the default output format to be used for # the MathJax output. For more details about the output format see MathJax # version 2 (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 # (see: # http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best # compatibility. This is the name for Mathjax version 2, for MathJax version 3 # this will be translated into chtml), NativeMML (i.e. MathML. Only supported # for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This # is the name for Mathjax version 3, for MathJax version 2 this will be # translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. The default value is: # - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 # - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # for MathJax version 2 (see # https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # For example for MathJax version 3 (see # http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): # MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: # http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /